@mayrlabs/setup-project 0.1.2 → 0.1.3

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.mjs ADDED
@@ -0,0 +1,619 @@
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
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __esm = (fn, res) => function __init() {
13
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
14
+ };
15
+ var __commonJS = (cb, mod) => function __require() {
16
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
17
+ };
18
+
19
+ // src/utils/pm.ts
20
+ import { execa } from "execa";
21
+ async function getPackageManager() {
22
+ const userAgent = process.env.npm_config_user_agent;
23
+ if (userAgent) {
24
+ if (userAgent.startsWith("yarn")) return "yarn";
25
+ if (userAgent.startsWith("pnpm")) return "pnpm";
26
+ if (userAgent.startsWith("bun")) return "bun";
27
+ }
28
+ return "npm";
29
+ }
30
+ async function installPackages(packages, dev = false) {
31
+ const pm = await getPackageManager();
32
+ const args = [];
33
+ if (pm === "npm") {
34
+ args.push("install");
35
+ if (dev) args.push("--save-dev");
36
+ } else if (pm === "yarn") {
37
+ args.push("add");
38
+ if (dev) args.push("-D");
39
+ } else if (pm === "pnpm") {
40
+ args.push("add");
41
+ if (dev) args.push("-D");
42
+ } else if (pm === "bun") {
43
+ args.push("add");
44
+ if (dev) args.push("-d");
45
+ }
46
+ await execa(pm, [...args, ...packages]);
47
+ }
48
+ var init_pm = __esm({
49
+ "src/utils/pm.ts"() {
50
+ "use strict";
51
+ }
52
+ });
53
+
54
+ // src/services/husky.ts
55
+ import { select, text } from "@clack/prompts";
56
+ import { execa as execa2 } from "execa";
57
+ import fs from "fs-extra";
58
+ async function promptHusky(config) {
59
+ const hookType = await select({
60
+ message: "What pre-commit hook would you like to use?",
61
+ options: [
62
+ { value: "lint-staged", label: "lint-staged" },
63
+ { value: "custom", label: "Custom script" },
64
+ { value: "none", label: "None" }
65
+ ]
66
+ });
67
+ config.huskyHookType = hookType;
68
+ if (hookType === "lint-staged") {
69
+ config.lintStaged = true;
70
+ } else if (hookType === "custom") {
71
+ const script = await text({
72
+ message: "Enter your custom pre-commit script:",
73
+ placeholder: "npm test",
74
+ validate(value) {
75
+ if (value.length === 0) return "Value is required!";
76
+ }
77
+ });
78
+ config.huskyCustomScript = script;
79
+ }
80
+ }
81
+ async function installHusky(config) {
82
+ await installPackages(["husky"], true);
83
+ try {
84
+ await execa2("npx", ["husky", "init"]);
85
+ } catch (e) {
86
+ await execa2("npm", ["pkg", "set", "scripts.prepare=husky"]);
87
+ await execa2("npm", ["run", "prepare"]);
88
+ }
89
+ if (config.huskyHookType === "lint-staged") {
90
+ await fs.outputFile(".husky/pre-commit", "npx lint-staged\n", {
91
+ mode: 493
92
+ });
93
+ } else if (config.huskyHookType === "custom" && config.huskyCustomScript) {
94
+ await fs.outputFile(".husky/pre-commit", `${config.huskyCustomScript}
95
+ `, {
96
+ mode: 493
97
+ });
98
+ }
99
+ }
100
+ var init_husky = __esm({
101
+ "src/services/husky.ts"() {
102
+ "use strict";
103
+ init_pm();
104
+ }
105
+ });
106
+
107
+ // src/services/formatter.ts
108
+ import { select as select2 } from "@clack/prompts";
109
+ import fs2 from "fs-extra";
110
+ async function promptFormatter(config) {
111
+ if (!config.formatterChoice) {
112
+ const formatter = await select2({
113
+ message: "Select a formatter:",
114
+ options: [
115
+ { value: "prettier", label: "Prettier" },
116
+ { value: "oxfmt", label: "Oxfmt" }
117
+ ]
118
+ });
119
+ config.formatterChoice = formatter;
120
+ }
121
+ }
122
+ async function installFormatter(config) {
123
+ if (config.formatterChoice === "prettier") {
124
+ await installPackages(["prettier"], true);
125
+ const configContent = {
126
+ semi: true,
127
+ singleQuote: true,
128
+ trailingComma: "all",
129
+ printWidth: 80,
130
+ tabWidth: 2
131
+ };
132
+ await fs2.writeJson(".prettierrc", configContent, { spaces: 2 });
133
+ } else if (config.formatterChoice === "oxfmt") {
134
+ await installPackages(["oxfmt"], true);
135
+ }
136
+ }
137
+ var init_formatter = __esm({
138
+ "src/services/formatter.ts"() {
139
+ "use strict";
140
+ init_pm();
141
+ }
142
+ });
143
+
144
+ // src/services/linter.ts
145
+ import { select as select3 } from "@clack/prompts";
146
+ import fs3 from "fs-extra";
147
+ async function promptLinter(config) {
148
+ if (!config.linterChoice) {
149
+ const linter = await select3({
150
+ message: "Select a linter:",
151
+ options: [
152
+ { value: "eslint", label: "ESLint" },
153
+ { value: "oxlint", label: "Oxlint" }
154
+ ]
155
+ });
156
+ config.linterChoice = linter;
157
+ }
158
+ }
159
+ async function installLinter(config) {
160
+ if (config.linterChoice === "eslint") {
161
+ await installPackages(["eslint"], true);
162
+ const configContent = {
163
+ extends: ["eslint:recommended"],
164
+ env: {
165
+ node: true,
166
+ es2021: true
167
+ },
168
+ parserOptions: {
169
+ ecmaVersion: "latest",
170
+ sourceType: "module"
171
+ }
172
+ };
173
+ await fs3.writeJson(".eslintrc.json", configContent, { spaces: 2 });
174
+ } else if (config.linterChoice === "oxlint") {
175
+ await installPackages(["oxlint"], true);
176
+ }
177
+ }
178
+ var init_linter = __esm({
179
+ "src/services/linter.ts"() {
180
+ "use strict";
181
+ init_pm();
182
+ }
183
+ });
184
+
185
+ // src/services/lint-staged.ts
186
+ import { multiselect } from "@clack/prompts";
187
+ import fs4 from "fs-extra";
188
+ async function promptLintStaged(config) {
189
+ const lintExtensions = await 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 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 fs4.writeJson(".lintstagedrc", lintStagedConfig, { spaces: 2 });
255
+ }
256
+ var init_lint_staged = __esm({
257
+ "src/services/lint-staged.ts"() {
258
+ "use strict";
259
+ init_pm();
260
+ init_formatter();
261
+ init_linter();
262
+ }
263
+ });
264
+
265
+ // src/services/env.ts
266
+ import { select as select5, confirm, text as text2, multiselect as multiselect2 } from "@clack/prompts";
267
+ import fs5 from "fs-extra";
268
+ import path from "path";
269
+ async function promptEnv(config) {
270
+ const variant = await select5({
271
+ message: "Which @t3-oss/env variant?",
272
+ options: [
273
+ { value: "@t3-oss/env-nextjs", label: "Next.js" },
274
+ { value: "@t3-oss/env-nuxt", label: "Nuxt" },
275
+ { value: "@t3-oss/env-core", label: "Core" }
276
+ ]
277
+ });
278
+ config.envVariant = variant;
279
+ const validator = await select5({
280
+ message: "Which validator?",
281
+ options: [
282
+ { value: "zod", label: "Zod" },
283
+ { value: "valibot", label: "Valibot" },
284
+ { value: "arktype", label: "Arktype" }
285
+ ]
286
+ });
287
+ config.envValidator = validator;
288
+ const installPresets = await confirm({
289
+ message: "Install presets?"
290
+ });
291
+ config.envInstallPresets = installPresets;
292
+ if (installPresets) {
293
+ const presets = await multiselect2({
294
+ message: "Select preset to extend:",
295
+ options: [
296
+ { value: "netlify", label: "Netlify" },
297
+ { value: "vercel", label: "Vercel" },
298
+ { value: "neonVercel", label: "Neon (Vercel)" },
299
+ { value: "supabaseVercel", label: "Supabase (Vercel)" },
300
+ { value: "uploadThing", label: "UploadThing" },
301
+ { value: "render", label: "Render" },
302
+ { value: "railway", label: "Railway" },
303
+ { value: "fly.io", label: "Fly.io" },
304
+ { value: "upstashRedis", label: "Upstash Redis" },
305
+ { value: "coolify", label: "Coolify" },
306
+ { value: "vite", label: "Vite" },
307
+ { value: "wxt", label: "WXT" }
308
+ ],
309
+ required: false
310
+ });
311
+ config.envPresets = presets;
312
+ }
313
+ const split = await select5({
314
+ message: "Split or Joined env files?",
315
+ options: [
316
+ { value: "split", label: "Split (env/server.ts, env/client.ts)" },
317
+ { value: "joined", label: "Joined (env.ts)" }
318
+ ]
319
+ });
320
+ config.envSplit = split;
321
+ const location = await text2({
322
+ message: "Where should the environment files be created?",
323
+ initialValue: "src/lib",
324
+ placeholder: "src/lib"
325
+ });
326
+ config.envLocation = location;
327
+ }
328
+ async function installEnv(config) {
329
+ await installPackages([config.envVariant, config.envValidator], true);
330
+ if (config.envInstallPresets) {
331
+ const presetPackage = `@t3-oss/env-core/presets-${config.envValidator}`;
332
+ await installPackages([presetPackage], true);
333
+ }
334
+ const targetDir = config.envLocation;
335
+ await fs5.ensureDir(targetDir);
336
+ const presetImport = config.envPresets && config.envPresets.length > 0 ? `// Presets: ${config.envPresets.join(", ")}
337
+ ` : "";
338
+ const content = `import { createEnv } from "${config.envVariant}";
339
+ import { ${config.envValidator} } from "${config.envValidator}";
340
+
341
+ ${presetImport}`;
342
+ if (config.envSplit === "split") {
343
+ await fs5.outputFile(
344
+ path.join(targetDir, "env/server.ts"),
345
+ `${content}
346
+ // Server env definition
347
+ export const env = createEnv({
348
+ server: {
349
+ // ...
350
+ },
351
+ experimental__runtimeEnv: process.env
352
+ });`
353
+ );
354
+ await fs5.outputFile(
355
+ path.join(targetDir, "env/client.ts"),
356
+ `${content}
357
+ // Client env definition
358
+ export const env = createEnv({
359
+ client: {
360
+ // ...
361
+ },
362
+ experimental__runtimeEnv: {
363
+ // ...
364
+ }
365
+ });`
366
+ );
367
+ } else {
368
+ await fs5.outputFile(
369
+ path.join(targetDir, "env.ts"),
370
+ `${content}
371
+ // Joined env definition
372
+ export const env = createEnv({
373
+ server: {
374
+ // ...
375
+ },
376
+ client: {
377
+ // ...
378
+ },
379
+ experimental__runtimeEnv: {
380
+ // ...
381
+ }
382
+ });`
383
+ );
384
+ }
385
+ }
386
+ var init_env = __esm({
387
+ "src/services/env.ts"() {
388
+ "use strict";
389
+ init_pm();
390
+ }
391
+ });
392
+
393
+ // src/utils/git.ts
394
+ import { execa as execa3 } from "execa";
395
+ async function isGitRepository() {
396
+ try {
397
+ await execa3("git", ["rev-parse", "--is-inside-work-tree"]);
398
+ return true;
399
+ } catch {
400
+ return false;
401
+ }
402
+ }
403
+ async function isGitDirty() {
404
+ try {
405
+ const { stdout } = await execa3("git", ["status", "--porcelain"]);
406
+ return stdout.length > 0;
407
+ } catch {
408
+ return false;
409
+ }
410
+ }
411
+ async function commitChanges(message) {
412
+ await execa3("git", ["add", "."]);
413
+ await execa3("git", ["commit", "-m", message]);
414
+ }
415
+ var init_git = __esm({
416
+ "src/utils/git.ts"() {
417
+ "use strict";
418
+ }
419
+ });
420
+
421
+ // package.json
422
+ var package_default;
423
+ var init_package = __esm({
424
+ "package.json"() {
425
+ package_default = {
426
+ name: "@mayrlabs/setup-project",
427
+ version: "0.1.3",
428
+ description: "Interactive CLI to setup project tools",
429
+ private: false,
430
+ publishConfig: {
431
+ access: "public"
432
+ },
433
+ keywords: [
434
+ "setup",
435
+ "cli",
436
+ "scaffold",
437
+ "husky",
438
+ "prettier",
439
+ "eslint"
440
+ ],
441
+ bin: {
442
+ "setup-project": "dist/index.js"
443
+ },
444
+ files: [
445
+ "dist",
446
+ "README.md",
447
+ "CHANGELOG.md",
448
+ "package.json"
449
+ ],
450
+ homepage: "https://github.com/MayR-Labs/mayrlabs-js/tree/main/packages/setup-project#readme",
451
+ bugs: {
452
+ url: "https://github.com/MayR-Labs/mayrlabs-js/issues"
453
+ },
454
+ repository: {
455
+ type: "git",
456
+ url: "git+https://github.com/MayR-Labs/mayrlabs-js.git",
457
+ directory: "packages/setup-project"
458
+ },
459
+ license: "MIT",
460
+ author: "Aghogho Meyoron <youngmayor.dev@gmail.com>",
461
+ type: "commonjs",
462
+ main: "dist/index.js",
463
+ scripts: {
464
+ build: "tsup",
465
+ demo: "tsx src/index.ts",
466
+ prepublishOnly: "npm run build"
467
+ },
468
+ dependencies: {
469
+ "@clack/prompts": "^0.7.0",
470
+ commander: "^11.1.0",
471
+ execa: "^8.0.1",
472
+ "fs-extra": "^11.2.0",
473
+ picocolors: "^1.0.0",
474
+ zod: "^3.22.4"
475
+ },
476
+ devDependencies: {
477
+ "@types/fs-extra": "^11.0.4",
478
+ "@types/node": "^20.11.16",
479
+ tsup: "^8.5.1",
480
+ typescript: "^5.3.3"
481
+ }
482
+ };
483
+ }
484
+ });
485
+
486
+ // src/index.ts
487
+ import {
488
+ intro,
489
+ outro,
490
+ multiselect as multiselect3,
491
+ isCancel,
492
+ cancel,
493
+ note,
494
+ confirm as confirm2,
495
+ spinner
496
+ } from "@clack/prompts";
497
+ import pc from "picocolors";
498
+ import { program } from "commander";
499
+ var require_index = __commonJS({
500
+ "src/index.ts"() {
501
+ init_husky();
502
+ init_formatter();
503
+ init_linter();
504
+ init_lint_staged();
505
+ init_env();
506
+ init_git();
507
+ init_package();
508
+ async function main() {
509
+ intro(pc.bgCyan(pc.black(" @mayrlabs/setup-project ")));
510
+ if (await isGitRepository()) {
511
+ if (await isGitDirty()) {
512
+ const shouldCommit = await confirm2({
513
+ message: "Your working directory is dirty. Would you like to commit changes before proceeding?"
514
+ });
515
+ if (shouldCommit) {
516
+ const message = await import("@clack/prompts").then(
517
+ (m) => m.text({
518
+ message: "Enter commit message:",
519
+ placeholder: "wip: pre-setup commit",
520
+ validate(value) {
521
+ if (value.length === 0) return "Commit message is required";
522
+ }
523
+ })
524
+ );
525
+ if (typeof message === "string") {
526
+ const s2 = spinner();
527
+ s2.start("Committing changes...");
528
+ await commitChanges(message);
529
+ s2.stop("Changes committed.");
530
+ }
531
+ }
532
+ }
533
+ }
534
+ const tools = await multiselect3({
535
+ message: "Select tools to configure:",
536
+ options: [
537
+ { value: "husky", label: "Husky" },
538
+ { value: "formatter", label: "Formatter (Prettier/Oxfmt)" },
539
+ { value: "linter", label: "Linter (Eslint/Oxlint)" },
540
+ { value: "lint-staged", label: "Lint-staged" },
541
+ { value: "env", label: "Env Validation (@t3-oss/env)" }
542
+ ],
543
+ required: false
544
+ });
545
+ if (isCancel(tools)) {
546
+ cancel("Operation cancelled.");
547
+ process.exit(0);
548
+ }
549
+ const selectedTools = tools;
550
+ const config = {
551
+ husky: selectedTools.includes("husky"),
552
+ formatter: selectedTools.includes("formatter"),
553
+ linter: selectedTools.includes("linter"),
554
+ lintStaged: selectedTools.includes("lint-staged"),
555
+ env: selectedTools.includes("env")
556
+ };
557
+ if (config.husky) await promptHusky(config);
558
+ if (config.formatter) await promptFormatter(config);
559
+ if (config.linter) await promptLinter(config);
560
+ if (config.lintStaged) await promptLintStaged(config);
561
+ if (config.env) await promptEnv(config);
562
+ let summary = "The following actions will be performed:\n\n";
563
+ if (config.husky) summary += "- Install and configure Husky\n";
564
+ if (config.formatter)
565
+ summary += `- Install and configure ${config.formatterChoice}
566
+ `;
567
+ if (config.linter)
568
+ summary += `- Install and configure ${config.linterChoice}
569
+ `;
570
+ if (config.lintStaged) summary += "- Install and configure Lint-staged\n";
571
+ if (config.env) summary += "- Install and configure @t3-oss/env\n";
572
+ note(summary, "Configuration Summary");
573
+ const proceed = await confirm2({
574
+ message: "Do you want to proceed with the installation?"
575
+ });
576
+ if (!proceed || isCancel(proceed)) {
577
+ cancel("Installation cancelled. Configuration saved.");
578
+ process.exit(0);
579
+ }
580
+ const s = spinner();
581
+ if (config.husky) {
582
+ s.start("Setting up Husky...");
583
+ await installHusky(config);
584
+ s.stop("Husky setup complete.");
585
+ }
586
+ if (config.formatter) {
587
+ s.start(`Setting up ${config.formatterChoice}...`);
588
+ await installFormatter(config);
589
+ s.stop(`${config.formatterChoice} setup complete.`);
590
+ }
591
+ if (config.linter) {
592
+ s.start(`Setting up ${config.linterChoice}...`);
593
+ await installLinter(config);
594
+ s.stop(`${config.linterChoice} setup complete.`);
595
+ }
596
+ if (config.lintStaged) {
597
+ s.start("Setting up Lint-staged...");
598
+ await installLintStaged(config);
599
+ s.stop("Lint-staged setup complete.");
600
+ }
601
+ if (config.env) {
602
+ s.start("Setting up Env Validation...");
603
+ await installEnv(config);
604
+ s.stop("Env Validation setup complete.");
605
+ }
606
+ outro(pc.green("Setup complete!"));
607
+ }
608
+ program.name("setup-project").description("Interactive setup for common project tools").version(package_default.version).action(main);
609
+ program.parse();
610
+ }
611
+ });
612
+ export default require_index();
613
+
614
+ /**
615
+ * Built with discipline by MayR Labs.
616
+ * Software should feel intentional.
617
+ */
618
+
619
+ //# sourceMappingURL=index.mjs.map