@mayrlabs/setup-project 0.1.2 → 0.1.4

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 ADDED
@@ -0,0 +1,779 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MayR Labs CLI
5
+ * Build. Ship. Repeat intelligently.
6
+ *
7
+ * (c) 2026 MayR Labs
8
+ * https://mayrlabs.com
9
+ */
10
+
11
+ "use strict";
12
+ var __create = Object.create;
13
+ var __defProp = Object.defineProperty;
14
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
15
+ var __getOwnPropNames = Object.getOwnPropertyNames;
16
+ var __getProtoOf = Object.getPrototypeOf;
17
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
27
+ // If the importer is in node compatibility mode or this is not an ESM
28
+ // file that has been converted to a CommonJS file using a Babel-
29
+ // compatible transform (i.e. "__esModule" has not been set), then set
30
+ // "default" to the CommonJS "module.exports" for node compatibility.
31
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
32
+ mod
33
+ ));
34
+
35
+ // src/index.ts
36
+ var import_prompts9 = require("@clack/prompts");
37
+ var import_picocolors = __toESM(require("picocolors"));
38
+ var import_commander = require("commander");
39
+
40
+ // src/services/husky.ts
41
+ var import_prompts = require("@clack/prompts");
42
+
43
+ // src/utils/pm.ts
44
+ var import_execa = require("execa");
45
+ async function getPackageManager() {
46
+ const userAgent = process.env.npm_config_user_agent;
47
+ if (userAgent) {
48
+ if (userAgent.startsWith("yarn")) return "yarn";
49
+ if (userAgent.startsWith("pnpm")) return "pnpm";
50
+ if (userAgent.startsWith("bun")) return "bun";
51
+ }
52
+ return "npm";
53
+ }
54
+ async function installPackages(packages, dev = false) {
55
+ const pm = await getPackageManager();
56
+ const args = [];
57
+ if (pm === "npm") {
58
+ args.push("install");
59
+ if (dev) args.push("--save-dev");
60
+ } else if (pm === "yarn") {
61
+ args.push("add");
62
+ if (dev) args.push("-D");
63
+ } else if (pm === "pnpm") {
64
+ args.push("add");
65
+ if (dev) args.push("-D");
66
+ } else if (pm === "bun") {
67
+ args.push("add");
68
+ if (dev) args.push("-d");
69
+ }
70
+ await (0, import_execa.execa)(pm, [...args, ...packages]);
71
+ }
72
+
73
+ // src/services/husky.ts
74
+ var import_execa2 = require("execa");
75
+ var import_fs_extra = __toESM(require("fs-extra"));
76
+ async function promptHusky(config) {
77
+ const hookType = await (0, import_prompts.select)({
78
+ message: "What pre-commit hook would you like to use?",
79
+ options: [
80
+ { value: "lint-staged", label: "lint-staged" },
81
+ { value: "custom", label: "Custom script" },
82
+ { value: "none", label: "None" }
83
+ ]
84
+ });
85
+ config.huskyHookType = hookType;
86
+ if (hookType === "lint-staged") {
87
+ config.lintStaged = true;
88
+ } else if (hookType === "custom") {
89
+ const script = await (0, import_prompts.text)({
90
+ message: "Enter your custom pre-commit script:",
91
+ placeholder: "npm test",
92
+ validate(value) {
93
+ if (value.length === 0) return "Value is required!";
94
+ }
95
+ });
96
+ config.huskyCustomScript = script;
97
+ }
98
+ }
99
+ async function installHusky(config) {
100
+ await installPackages(["husky"], true);
101
+ try {
102
+ await (0, import_execa2.execa)("npx", ["husky", "init"]);
103
+ } catch (e) {
104
+ await (0, import_execa2.execa)("npm", ["pkg", "set", "scripts.prepare=husky"]);
105
+ await (0, import_execa2.execa)("npm", ["run", "prepare"]);
106
+ }
107
+ if (config.huskyHookType === "lint-staged") {
108
+ await import_fs_extra.default.outputFile(".husky/pre-commit", "npx lint-staged\n", {
109
+ mode: 493
110
+ });
111
+ } else if (config.huskyHookType === "custom" && config.huskyCustomScript) {
112
+ await import_fs_extra.default.outputFile(".husky/pre-commit", `${config.huskyCustomScript}
113
+ `, {
114
+ mode: 493
115
+ });
116
+ }
117
+ }
118
+
119
+ // src/services/formatter.ts
120
+ var import_prompts2 = require("@clack/prompts");
121
+ var import_fs_extra2 = __toESM(require("fs-extra"));
122
+ async function promptFormatter(config) {
123
+ if (!config.formatterChoice) {
124
+ const formatter = await (0, import_prompts2.select)({
125
+ message: "Select a formatter:",
126
+ options: [
127
+ { value: "prettier", label: "Prettier" },
128
+ { value: "oxfmt", label: "Oxfmt" }
129
+ ]
130
+ });
131
+ config.formatterChoice = formatter;
132
+ }
133
+ }
134
+ async function installFormatter(config) {
135
+ if (config.formatterChoice === "prettier") {
136
+ await installPackages(["prettier"], true);
137
+ const configContent = {
138
+ semi: true,
139
+ singleQuote: true,
140
+ trailingComma: "all",
141
+ printWidth: 80,
142
+ tabWidth: 2
143
+ };
144
+ await import_fs_extra2.default.writeJson(".prettierrc", configContent, { spaces: 2 });
145
+ } else if (config.formatterChoice === "oxfmt") {
146
+ await installPackages(["oxfmt"], true);
147
+ }
148
+ }
149
+
150
+ // src/services/linter.ts
151
+ var import_prompts3 = require("@clack/prompts");
152
+ var import_fs_extra3 = __toESM(require("fs-extra"));
153
+ async function promptLinter(config) {
154
+ if (!config.linterChoice) {
155
+ const linter = await (0, import_prompts3.select)({
156
+ message: "Select a linter:",
157
+ options: [
158
+ { value: "eslint", label: "ESLint" },
159
+ { value: "oxlint", label: "Oxlint" }
160
+ ]
161
+ });
162
+ config.linterChoice = linter;
163
+ }
164
+ }
165
+ async function installLinter(config) {
166
+ if (config.linterChoice === "eslint") {
167
+ await installPackages(["eslint"], true);
168
+ const configContent = {
169
+ extends: ["eslint:recommended"],
170
+ env: {
171
+ node: true,
172
+ es2021: true
173
+ },
174
+ parserOptions: {
175
+ ecmaVersion: "latest",
176
+ sourceType: "module"
177
+ }
178
+ };
179
+ await import_fs_extra3.default.writeJson(".eslintrc.json", configContent, { spaces: 2 });
180
+ } else if (config.linterChoice === "oxlint") {
181
+ await installPackages(["oxlint"], true);
182
+ }
183
+ }
184
+
185
+ // src/services/lint-staged.ts
186
+ var import_prompts4 = require("@clack/prompts");
187
+ var import_fs_extra4 = __toESM(require("fs-extra"));
188
+ async function promptLintStaged(config) {
189
+ const lintExtensions = await (0, import_prompts4.multiselect)({
190
+ message: "Select extensions to lint:",
191
+ options: [
192
+ { value: "js", label: "js" },
193
+ { value: "ts", label: "ts" },
194
+ { value: "jsx", label: "jsx" },
195
+ { value: "tsx", label: "tsx" },
196
+ { value: "html", label: "html" },
197
+ { value: "vue", label: "vue" },
198
+ { value: "svelte", label: "svelte" }
199
+ ],
200
+ required: false
201
+ });
202
+ const formatExtensions = await (0, import_prompts4.multiselect)({
203
+ message: "Select extensions to format:",
204
+ options: [
205
+ { value: "md", label: "md" },
206
+ { value: "css", label: "css" },
207
+ { value: "scss", label: "scss" },
208
+ { value: "json", label: "json" },
209
+ { value: "yaml", label: "yaml" },
210
+ { value: "html", label: "html" },
211
+ { value: "js", label: "js" },
212
+ { value: "ts", label: "ts" },
213
+ { value: "jsx", label: "jsx" },
214
+ { value: "tsx", label: "tsx" },
215
+ { value: "vue", label: "vue" },
216
+ { value: "svelte", label: "svelte" }
217
+ ],
218
+ required: false
219
+ });
220
+ config.lintStagedLintExtensions = lintExtensions;
221
+ config.lintStagedFormatExtensions = formatExtensions;
222
+ if (lintExtensions.length > 0 && !config.linterChoice) {
223
+ await promptLinter(config);
224
+ config.linter = true;
225
+ }
226
+ if (formatExtensions.length > 0 && !config.formatterChoice) {
227
+ await promptFormatter(config);
228
+ config.formatter = true;
229
+ }
230
+ }
231
+ async function installLintStaged(config) {
232
+ await installPackages(["lint-staged"], true);
233
+ const lintStagedConfig = {};
234
+ const lintExts = config.lintStagedLintExtensions || [];
235
+ const formatExts = config.lintStagedFormatExtensions || [];
236
+ if (lintExts.length > 0) {
237
+ await installLinter(config);
238
+ const glob = `*.{${lintExts.join(",")}}`;
239
+ if (config.linterChoice === "oxlint") {
240
+ lintStagedConfig[glob] = ["npx oxlint --fix"];
241
+ } else {
242
+ lintStagedConfig[glob] = ["eslint --fix"];
243
+ }
244
+ }
245
+ if (formatExts.length > 0) {
246
+ await installFormatter(config);
247
+ const glob = `*.{${formatExts.join(",")}}`;
248
+ if (config.formatterChoice === "oxfmt") {
249
+ lintStagedConfig[glob] = ["npx oxfmt"];
250
+ } else {
251
+ lintStagedConfig[glob] = ["prettier --write"];
252
+ }
253
+ }
254
+ await import_fs_extra4.default.writeJson(".lintstagedrc", lintStagedConfig, { spaces: 2 });
255
+ }
256
+
257
+ // src/services/env.ts
258
+ var import_prompts5 = require("@clack/prompts");
259
+ var import_fs_extra5 = __toESM(require("fs-extra"));
260
+ var import_path = __toESM(require("path"));
261
+ async function promptEnv(config) {
262
+ const variant = await (0, import_prompts5.select)({
263
+ message: "Which @t3-oss/env variant?",
264
+ options: [
265
+ { value: "@t3-oss/env-nextjs", label: "Next.js" },
266
+ { value: "@t3-oss/env-nuxt", label: "Nuxt" },
267
+ { value: "@t3-oss/env-core", label: "Core" }
268
+ ]
269
+ });
270
+ config.envVariant = variant;
271
+ const validator = await (0, import_prompts5.select)({
272
+ message: "Which validator?",
273
+ options: [
274
+ { value: "zod", label: "Zod" },
275
+ { value: "valibot", label: "Valibot" },
276
+ { value: "arktype", label: "Arktype" }
277
+ ]
278
+ });
279
+ config.envValidator = validator;
280
+ const installPresets = await (0, import_prompts5.confirm)({
281
+ message: "Install presets?"
282
+ });
283
+ config.envInstallPresets = installPresets;
284
+ if (installPresets) {
285
+ const presets = await (0, import_prompts5.multiselect)({
286
+ message: "Select preset to extend:",
287
+ options: [
288
+ { value: "netlify", label: "Netlify" },
289
+ { value: "vercel", label: "Vercel" },
290
+ { value: "neonVercel", label: "Neon (Vercel)" },
291
+ { value: "supabaseVercel", label: "Supabase (Vercel)" },
292
+ { value: "uploadThing", label: "UploadThing" },
293
+ { value: "render", label: "Render" },
294
+ { value: "railway", label: "Railway" },
295
+ { value: "fly.io", label: "Fly.io" },
296
+ { value: "upstashRedis", label: "Upstash Redis" },
297
+ { value: "coolify", label: "Coolify" },
298
+ { value: "vite", label: "Vite" },
299
+ { value: "wxt", label: "WXT" }
300
+ ],
301
+ required: false
302
+ });
303
+ config.envPresets = presets;
304
+ }
305
+ const split = await (0, import_prompts5.select)({
306
+ message: "Split or Joined env files?",
307
+ options: [
308
+ { value: "split", label: "Split (env/server.ts, env/client.ts)" },
309
+ { value: "joined", label: "Joined (env.ts)" }
310
+ ]
311
+ });
312
+ config.envSplit = split;
313
+ const location = await (0, import_prompts5.text)({
314
+ message: "Where should the environment files be created?",
315
+ initialValue: "src/lib",
316
+ placeholder: "src/lib"
317
+ });
318
+ config.envLocation = location;
319
+ }
320
+ async function installEnv(config) {
321
+ await installPackages([config.envVariant, config.envValidator], true);
322
+ if (config.envInstallPresets) {
323
+ const presetPackage = `@t3-oss/env-core/presets-${config.envValidator}`;
324
+ await installPackages([presetPackage], true);
325
+ }
326
+ const targetDir = config.envLocation;
327
+ await import_fs_extra5.default.ensureDir(targetDir);
328
+ const presetImport = config.envPresets && config.envPresets.length > 0 ? `// Presets: ${config.envPresets.join(", ")}
329
+ ` : "";
330
+ const content = `import { createEnv } from "${config.envVariant}";
331
+ import { ${config.envValidator} } from "${config.envValidator}";
332
+
333
+ ${presetImport}`;
334
+ if (config.envSplit === "split") {
335
+ await import_fs_extra5.default.outputFile(
336
+ import_path.default.join(targetDir, "env/server.ts"),
337
+ `${content}
338
+ // Server env definition
339
+ export const env = createEnv({
340
+ server: {
341
+ // ...
342
+ },
343
+ experimental__runtimeEnv: process.env
344
+ });`
345
+ );
346
+ await import_fs_extra5.default.outputFile(
347
+ import_path.default.join(targetDir, "env/client.ts"),
348
+ `${content}
349
+ // Client env definition
350
+ export const env = createEnv({
351
+ client: {
352
+ // ...
353
+ },
354
+ experimental__runtimeEnv: {
355
+ // ...
356
+ }
357
+ });`
358
+ );
359
+ } else {
360
+ await import_fs_extra5.default.outputFile(
361
+ import_path.default.join(targetDir, "env.ts"),
362
+ `${content}
363
+ // Joined env definition
364
+ export const env = createEnv({
365
+ server: {
366
+ // ...
367
+ },
368
+ client: {
369
+ // ...
370
+ },
371
+ experimental__runtimeEnv: {
372
+ // ...
373
+ }
374
+ });`
375
+ );
376
+ }
377
+ }
378
+
379
+ // src/services/test.ts
380
+ var import_prompts6 = require("@clack/prompts");
381
+ var import_fs_extra6 = __toESM(require("fs-extra"));
382
+ async function promptTest(config) {
383
+ const runner = await (0, import_prompts6.select)({
384
+ message: "Select a test runner:",
385
+ options: [
386
+ { value: "vitest", label: "Vitest" },
387
+ { value: "jest", label: "Jest" }
388
+ ]
389
+ });
390
+ config.testRunner = runner;
391
+ }
392
+ async function installTest(config) {
393
+ if (config.testRunner === "vitest") {
394
+ await installPackages(["vitest"], true);
395
+ const configFile = "vitest.config.ts";
396
+ if (!await import_fs_extra6.default.pathExists(configFile)) {
397
+ await import_fs_extra6.default.outputFile(
398
+ configFile,
399
+ `import { defineConfig } from 'vitest/config';
400
+
401
+ export default defineConfig({
402
+ test: {
403
+ environment: 'node',
404
+ },
405
+ });
406
+ `
407
+ );
408
+ }
409
+ } else if (config.testRunner === "jest") {
410
+ await installPackages(["jest", "ts-jest", "@types/jest"], true);
411
+ const configFile = "jest.config.js";
412
+ if (!await import_fs_extra6.default.pathExists(configFile)) {
413
+ await import_fs_extra6.default.outputFile(
414
+ configFile,
415
+ `/** @type {import('ts-jest').JestConfigWithTsJest} */
416
+ module.exports = {
417
+ preset: 'ts-jest',
418
+ testEnvironment: 'node',
419
+ };
420
+ `
421
+ );
422
+ }
423
+ }
424
+ }
425
+
426
+ // src/services/editor-config.ts
427
+ var import_prompts7 = require("@clack/prompts");
428
+ var import_fs_extra7 = __toESM(require("fs-extra"));
429
+ async function promptEditorConfig(config) {
430
+ const preset = await (0, import_prompts7.select)({
431
+ message: "Select EditorConfig preset:",
432
+ options: [
433
+ { value: "default", label: "Default (Spaces 2)" },
434
+ { value: "spaces4", label: "Spaces 4" },
435
+ { value: "tabs", label: "Tabs" }
436
+ ]
437
+ });
438
+ config.editorConfigPreset = preset;
439
+ }
440
+ async function installEditorConfig(config) {
441
+ let content = "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n";
442
+ if (config.editorConfigPreset === "default" || config.editorConfigPreset === "spaces2") {
443
+ content += "indent_style = space\nindent_size = 2\n";
444
+ } else if (config.editorConfigPreset === "spaces4") {
445
+ content += "indent_style = space\nindent_size = 4\n";
446
+ } else if (config.editorConfigPreset === "tabs") {
447
+ content += "indent_style = tab\n";
448
+ }
449
+ await import_fs_extra7.default.outputFile(".editorconfig", content);
450
+ }
451
+
452
+ // src/services/license.ts
453
+ var import_prompts8 = require("@clack/prompts");
454
+ var import_fs_extra8 = __toESM(require("fs-extra"));
455
+ var import_path2 = __toESM(require("path"));
456
+ async function promptLicense(config) {
457
+ const name = await (0, import_prompts8.text)({
458
+ message: "License Holder Name:",
459
+ placeholder: "John Doe",
460
+ initialValue: config.authorName || ""
461
+ });
462
+ config.licenseName = name;
463
+ const email = await (0, import_prompts8.text)({
464
+ message: "License Holder Email:",
465
+ placeholder: "john@example.com"
466
+ });
467
+ config.licenseEmail = email;
468
+ const website = await (0, import_prompts8.text)({
469
+ message: "License Holder Website:",
470
+ placeholder: "https://example.com"
471
+ });
472
+ config.licenseWebsite = website;
473
+ const type = await (0, import_prompts8.select)({
474
+ message: "Select License Type:",
475
+ options: [
476
+ { value: "MIT", label: "MIT" },
477
+ { value: "ISC", label: "ISC" },
478
+ { value: "Apache-2.0", label: "Apache 2.0" },
479
+ { value: "UNLICENSED", label: "UNLICENSED" }
480
+ ]
481
+ });
482
+ config.licenseType = type;
483
+ }
484
+ async function installLicense(config) {
485
+ if (config.licenseType !== "UNLICENSED") {
486
+ const year = (/* @__PURE__ */ new Date()).getFullYear().toString();
487
+ const templatePath = import_path2.default.join(
488
+ __dirname,
489
+ "licenses",
490
+ `${config.licenseType}.txt`
491
+ );
492
+ if (await import_fs_extra8.default.pathExists(templatePath)) {
493
+ let licenseContent = await import_fs_extra8.default.readFile(templatePath, "utf-8");
494
+ licenseContent = licenseContent.replace(/{YEAR}/g, year);
495
+ licenseContent = licenseContent.replace(/{HOLDER}/g, config.licenseName);
496
+ licenseContent = licenseContent.replace(/{EMAIL}/g, config.licenseEmail);
497
+ licenseContent = licenseContent.replace(
498
+ /{WEBSITE}/g,
499
+ config.licenseWebsite
500
+ );
501
+ await import_fs_extra8.default.outputFile("LICENSE", licenseContent);
502
+ } else {
503
+ const simpleContent = `Copyright (c) ${year} ${config.licenseName}
504
+ Licensed under ${config.licenseType}`;
505
+ await import_fs_extra8.default.outputFile("LICENSE", simpleContent);
506
+ }
507
+ }
508
+ if (await import_fs_extra8.default.pathExists("package.json")) {
509
+ const pkg = await import_fs_extra8.default.readJson("package.json");
510
+ pkg.license = config.licenseType;
511
+ if (config.licenseName) {
512
+ pkg.author = `${config.licenseName} <${config.licenseEmail}> (${config.licenseWebsite})`;
513
+ }
514
+ await import_fs_extra8.default.writeJson("package.json", pkg, { spaces: 2 });
515
+ }
516
+ }
517
+
518
+ // src/utils/git.ts
519
+ var import_execa3 = require("execa");
520
+ async function isGitRepository() {
521
+ try {
522
+ await (0, import_execa3.execa)("git", ["rev-parse", "--is-inside-work-tree"]);
523
+ return true;
524
+ } catch {
525
+ return false;
526
+ }
527
+ }
528
+ async function isGitDirty() {
529
+ try {
530
+ const { stdout } = await (0, import_execa3.execa)("git", ["status", "--porcelain"]);
531
+ return stdout.length > 0;
532
+ } catch {
533
+ return false;
534
+ }
535
+ }
536
+ async function commitChanges(message) {
537
+ await (0, import_execa3.execa)("git", ["add", "."]);
538
+ await (0, import_execa3.execa)("git", ["commit", "-m", message]);
539
+ }
540
+
541
+ // package.json
542
+ var package_default = {
543
+ name: "@mayrlabs/setup-project",
544
+ version: "0.1.4",
545
+ description: "Interactive CLI to setup project tools",
546
+ private: false,
547
+ publishConfig: {
548
+ access: "public"
549
+ },
550
+ keywords: [
551
+ "setup",
552
+ "cli",
553
+ "scaffold",
554
+ "husky",
555
+ "prettier",
556
+ "eslint"
557
+ ],
558
+ bin: {
559
+ "setup-project": "dist/index.js"
560
+ },
561
+ files: [
562
+ "dist",
563
+ "README.md",
564
+ "CHANGELOG.md",
565
+ "package.json"
566
+ ],
567
+ homepage: "https://github.com/MayR-Labs/mayrlabs-js/tree/main/packages/setup-project#readme",
568
+ bugs: {
569
+ url: "https://github.com/MayR-Labs/mayrlabs-js/issues"
570
+ },
571
+ repository: {
572
+ type: "git",
573
+ url: "git+https://github.com/MayR-Labs/mayrlabs-js.git",
574
+ directory: "packages/setup-project"
575
+ },
576
+ license: "MIT",
577
+ author: {
578
+ name: "Aghogho Meyoron",
579
+ email: "youngmayor.dev@gmail.com",
580
+ url: "https://mayrlabs.com"
581
+ },
582
+ type: "commonjs",
583
+ main: "dist/index.js",
584
+ scripts: {
585
+ build: "tsup",
586
+ demo: "tsx src/index.ts",
587
+ prepublishOnly: "npm run build"
588
+ },
589
+ dependencies: {
590
+ "@clack/prompts": "^0.7.0",
591
+ commander: "^11.1.0",
592
+ execa: "^8.0.1",
593
+ "fs-extra": "^11.2.0",
594
+ picocolors: "^1.0.0",
595
+ zod: "^3.22.4"
596
+ },
597
+ devDependencies: {
598
+ "@types/fs-extra": "^11.0.4",
599
+ "@types/node": "^20.11.16",
600
+ tsup: "^8.5.1",
601
+ typescript: "^5.3.3"
602
+ }
603
+ };
604
+
605
+ // src/utils/logger.ts
606
+ var import_fs_extra9 = __toESM(require("fs-extra"));
607
+ var import_path3 = __toESM(require("path"));
608
+ var LOG_DIR = ".mayrlabs/setup-project";
609
+ var ERRORS_DIR = import_path3.default.join(LOG_DIR, "errors");
610
+ async function logError(error) {
611
+ try {
612
+ await import_fs_extra9.default.ensureDir(ERRORS_DIR);
613
+ const gitignorePath = import_path3.default.join(LOG_DIR, ".gitignore");
614
+ if (!await import_fs_extra9.default.pathExists(gitignorePath)) {
615
+ await import_fs_extra9.default.outputFile(gitignorePath, "*\n");
616
+ }
617
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
618
+ const logFile = import_path3.default.join(ERRORS_DIR, `log-${timestamp}.txt`);
619
+ const errorMessage = error instanceof Error ? error.stack || error.message : String(error);
620
+ const logContent = `Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}
621
+
622
+ Error:
623
+ ${errorMessage}
624
+ `;
625
+ await import_fs_extra9.default.outputFile(logFile, logContent);
626
+ return logFile;
627
+ } catch (e) {
628
+ console.error("Failed to log error:", e);
629
+ return "";
630
+ }
631
+ }
632
+
633
+ // src/index.ts
634
+ async function main() {
635
+ try {
636
+ (0, import_prompts9.intro)(import_picocolors.default.bgCyan(import_picocolors.default.black(" @mayrlabs/setup-project ")));
637
+ if (await isGitRepository()) {
638
+ if (await isGitDirty()) {
639
+ const shouldCommit = await (0, import_prompts9.confirm)({
640
+ message: "Your working directory is dirty. Would you like to commit changes before proceeding?"
641
+ });
642
+ if (shouldCommit) {
643
+ const message = await import("@clack/prompts").then(
644
+ (m) => m.text({
645
+ message: "Enter commit message:",
646
+ placeholder: "wip: pre-setup commit",
647
+ validate(value) {
648
+ if (value.length === 0) return "Commit message is required";
649
+ }
650
+ })
651
+ );
652
+ if (typeof message === "string") {
653
+ const s2 = (0, import_prompts9.spinner)();
654
+ s2.start("Committing changes...");
655
+ await commitChanges(message);
656
+ s2.stop("Changes committed.");
657
+ }
658
+ }
659
+ }
660
+ }
661
+ const tools = await (0, import_prompts9.multiselect)({
662
+ message: "Select tools to configure:",
663
+ options: [
664
+ { value: "husky", label: "Husky" },
665
+ { value: "formatter", label: "Formatter (Prettier/Oxfmt)" },
666
+ { value: "linter", label: "Linter (Eslint/Oxlint)" },
667
+ { value: "lint-staged", label: "Lint-staged" },
668
+ { value: "env", label: "Env Validation (@t3-oss/env)" },
669
+ { value: "test", label: "Test Runner (Vitest/Jest)" },
670
+ { value: "editorConfig", label: "EditorConfig" },
671
+ { value: "license", label: "License" }
672
+ ],
673
+ required: false
674
+ });
675
+ if ((0, import_prompts9.isCancel)(tools)) {
676
+ (0, import_prompts9.cancel)("Operation cancelled.");
677
+ process.exit(0);
678
+ }
679
+ const selectedTools = tools;
680
+ const config = {
681
+ husky: selectedTools.includes("husky"),
682
+ formatter: selectedTools.includes("formatter"),
683
+ linter: selectedTools.includes("linter"),
684
+ lintStaged: selectedTools.includes("lint-staged"),
685
+ env: selectedTools.includes("env"),
686
+ test: selectedTools.includes("test"),
687
+ editorConfig: selectedTools.includes("editorConfig"),
688
+ license: selectedTools.includes("license")
689
+ };
690
+ if (config.husky) await promptHusky(config);
691
+ if (config.formatter) await promptFormatter(config);
692
+ if (config.linter) await promptLinter(config);
693
+ if (config.lintStaged) await promptLintStaged(config);
694
+ if (config.env) await promptEnv(config);
695
+ if (config.test) await promptTest(config);
696
+ if (config.editorConfig) await promptEditorConfig(config);
697
+ if (config.license) await promptLicense(config);
698
+ let summary = "The following actions will be performed:\n\n";
699
+ if (config.husky) summary += "- Install and configure Husky\n";
700
+ if (config.formatter)
701
+ summary += `- Install and configure ${config.formatterChoice}
702
+ `;
703
+ if (config.linter)
704
+ summary += `- Install and configure ${config.linterChoice}
705
+ `;
706
+ if (config.lintStaged) summary += "- Install and configure Lint-staged\n";
707
+ if (config.env) summary += "- Install and configure @t3-oss/env\n";
708
+ if (config.test)
709
+ summary += `- Install and configure ${config.testRunner}
710
+ `;
711
+ if (config.editorConfig) summary += "- Create .editorconfig\n";
712
+ if (config.license) summary += `- Create LICENSE (${config.licenseType})
713
+ `;
714
+ (0, import_prompts9.note)(summary, "Configuration Summary");
715
+ const proceed = await (0, import_prompts9.confirm)({
716
+ message: "Do you want to proceed with the installation?"
717
+ });
718
+ if (!proceed || (0, import_prompts9.isCancel)(proceed)) {
719
+ (0, import_prompts9.cancel)("Installation cancelled. Configuration saved.");
720
+ process.exit(0);
721
+ }
722
+ const s = (0, import_prompts9.spinner)();
723
+ if (config.husky) {
724
+ s.start("Setting up Husky...");
725
+ await installHusky(config);
726
+ s.stop("Husky setup complete.");
727
+ }
728
+ if (config.formatter) {
729
+ s.start(`Setting up ${config.formatterChoice}...`);
730
+ await installFormatter(config);
731
+ s.stop(`${config.formatterChoice} setup complete.`);
732
+ }
733
+ if (config.linter) {
734
+ s.start(`Setting up ${config.linterChoice}...`);
735
+ await installLinter(config);
736
+ s.stop(`${config.linterChoice} setup complete.`);
737
+ }
738
+ if (config.lintStaged) {
739
+ s.start("Setting up Lint-staged...");
740
+ await installLintStaged(config);
741
+ s.stop("Lint-staged setup complete.");
742
+ }
743
+ if (config.env) {
744
+ s.start("Setting up Env Validation...");
745
+ await installEnv(config);
746
+ s.stop("Env Validation setup complete.");
747
+ }
748
+ if (config.test) {
749
+ s.start(`Setting up ${config.testRunner}...`);
750
+ await installTest(config);
751
+ s.stop(`${config.testRunner} setup complete.`);
752
+ }
753
+ if (config.editorConfig) {
754
+ s.start("Creating .editorconfig...");
755
+ await installEditorConfig(config);
756
+ s.stop(".editorconfig created.");
757
+ }
758
+ if (config.license) {
759
+ s.start("Creating LICENSE...");
760
+ await installLicense(config);
761
+ s.stop("LICENSE created.");
762
+ }
763
+ (0, import_prompts9.outro)(import_picocolors.default.green("Setup complete!"));
764
+ } catch (error) {
765
+ const logPath = await logError(error);
766
+ (0, import_prompts9.outro)(import_picocolors.default.red(`
767
+ Something went wrong!
768
+ Error log saved to: ${logPath}`));
769
+ process.exit(1);
770
+ }
771
+ }
772
+ import_commander.program.name("setup-project").description("Interactive setup for common project tools").version(package_default.version).action(main);
773
+ import_commander.program.parse();
774
+
775
+ /**
776
+ * Built with discipline by MayR Labs.
777
+ * Software should feel intentional.
778
+ */
779
+