rebar-js 1.0.0

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,1319 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/prompts/basic.ts
7
+ import { select, input } from "@inquirer/prompts";
8
+
9
+ // src/utils/display.ts
10
+ var isColorSupported = process.stdout.isTTY && !process.env.NO_COLOR;
11
+ var style = (open, close = 39) => {
12
+ const c = open === 1 ? 22 : open === 2 ? 22 : close;
13
+ return (s) => isColorSupported ? `\x1B[${open}m${s}\x1B[${c}m` : s;
14
+ };
15
+ var colors = {
16
+ cyan: style(36),
17
+ green: style(32),
18
+ red: style(31),
19
+ yellow: style(33),
20
+ blue: style(34),
21
+ magenta: style(35),
22
+ gray: style(90),
23
+ bold: style(1),
24
+ dim: style(2)
25
+ };
26
+ var SYM = {
27
+ check: "\u2713",
28
+ cross: "\u2717",
29
+ arrow: "\u276F",
30
+ bullet: "\u25CF"
31
+ };
32
+ function banner() {
33
+ console.log(`
34
+ ${colors.cyan(colors.bold("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"))}
35
+ ${colors.cyan("\u2551")} ${colors.magenta(colors.bold("R E B A R"))} ${colors.cyan("\u2551")}
36
+ ${colors.cyan("\u2551")} ${colors.gray("project scaffold CLI")} ${colors.cyan("\u2551")}
37
+ ${colors.cyan(colors.bold("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2559"))}
38
+ `);
39
+ }
40
+ function step(num, msg) {
41
+ console.log(`
42
+ ${colors.cyan(colors.bold(`${SYM.arrow} Step ${num}`))}${colors.gray(":")} ${colors.bold(msg)}`);
43
+ console.log(colors.gray(` ${"\u2500".repeat(50)}`));
44
+ }
45
+ function success(msg) {
46
+ console.log(` ${colors.green(`${SYM.check} ${msg}`)}`);
47
+ }
48
+ function error(msg) {
49
+ console.log(` ${colors.red(`${SYM.cross} ${msg}`)}`);
50
+ }
51
+ function info(msg) {
52
+ console.log(` ${colors.blue("\u2139")} ${msg}`);
53
+ }
54
+ function done() {
55
+ console.log(`
56
+ ${colors.green(colors.bold(`${SYM.check} All done!`))}
57
+ ${colors.gray("Happy coding!")}
58
+ `);
59
+ }
60
+
61
+ // src/prompts/basic.ts
62
+ async function choosePackageManager() {
63
+ return await select({
64
+ message: `${colors.cyan("\u276F")} Pick a package manager`,
65
+ choices: [
66
+ {
67
+ name: "npm",
68
+ value: "npm",
69
+ description: "Node package manager"
70
+ },
71
+ {
72
+ name: "yarn",
73
+ value: "yarn",
74
+ description: "Fast, reliable, and secure dependency management"
75
+ },
76
+ {
77
+ name: "bun",
78
+ value: "bun",
79
+ description: "All-in-one JavaScript runtime & package manager"
80
+ },
81
+ {
82
+ name: "pnpm",
83
+ value: "pnpm",
84
+ description: "Fast, disk-space efficient package manager",
85
+ disabled: "(not supported \u2014 pnpm install --ignore-scripts breaks builds)"
86
+ }
87
+ ]
88
+ });
89
+ }
90
+ async function chooseFramework(manager) {
91
+ return await select({
92
+ message: `${colors.cyan("\u276F")} Pick a framework`,
93
+ choices: [
94
+ {
95
+ name: "Next.js",
96
+ value: "nextjs",
97
+ description: "React framework with SSR, app router, and more",
98
+ disabled: manager === "pnpm" ? "(not recommended with pnpm)" : false
99
+ },
100
+ {
101
+ name: "Vite (React)",
102
+ value: "vite",
103
+ description: "Lightning-fast React SPA with Vite"
104
+ },
105
+ {
106
+ name: "Express",
107
+ value: "express",
108
+ description: "Minimalist Node.js backend framework"
109
+ },
110
+ {
111
+ name: "Expo",
112
+ value: "expo",
113
+ description: "Universal React Native apps for iOS, Android, and web"
114
+ },
115
+ {
116
+ name: "Electron",
117
+ value: "electron",
118
+ description: "Desktop apps with web technologies",
119
+ disabled: manager === "bun" ? "(not supported with bun)" : false
120
+ }
121
+ ]
122
+ });
123
+ }
124
+ async function typeProjectName() {
125
+ const str = await input({
126
+ message: `${colors.cyan("\u276F")} Project name`,
127
+ default: "my-app"
128
+ });
129
+ return str.trim() || "my-app";
130
+ }
131
+
132
+ // src/prompts/electronFramework.ts
133
+ import { select as select2 } from "@inquirer/prompts";
134
+ var electronFramework = async () => {
135
+ return await select2({
136
+ message: `${colors.cyan("\u276F")} Pick a build tool for Electron`,
137
+ choices: [
138
+ {
139
+ name: "Electron Forge",
140
+ value: "electron forge",
141
+ description: "Official all-in-one toolchain for Electron"
142
+ },
143
+ {
144
+ name: "Electron Vite",
145
+ value: "electron vite",
146
+ description: "Fast HMR with Vite for Electron"
147
+ }
148
+ ]
149
+ });
150
+ };
151
+
152
+ // src/setup/electron.ts
153
+ import { spawn } from "child_process";
154
+ var buildElectronApp = async (manager) => {
155
+ step(1, "Build tool");
156
+ await electronFramework();
157
+ step(2, "Scaffolding Electron app");
158
+ info("Launching interactive Electron quick-start...\n");
159
+ const child = spawn(manager, ["create", "@quick-start/electron"], {
160
+ stdio: "inherit",
161
+ cwd: process.cwd(),
162
+ shell: true
163
+ });
164
+ child.on("close", (code) => process.exit(code ?? 0));
165
+ };
166
+
167
+ // src/setup/expo.ts
168
+ import { spawn as spawn2 } from "child_process";
169
+ var buildExpoApp = (manager) => {
170
+ step(1, "Scaffolding Expo app");
171
+ info("Launching interactive Expo setup...\n");
172
+ const child = spawn2(
173
+ manager === "npm" ? "npx" : manager,
174
+ ["create", "expo-app"],
175
+ {
176
+ stdio: "inherit",
177
+ shell: true,
178
+ cwd: process.cwd()
179
+ }
180
+ );
181
+ child.on("close", (code) => {
182
+ process.exit(code ?? 0);
183
+ });
184
+ };
185
+
186
+ // src/prompts/express.ts
187
+ import { checkbox } from "@inquirer/prompts";
188
+ var expressPrompts = async (manager) => {
189
+ return await checkbox({
190
+ message: `${colors.cyan("\u276F")} Extra tools to include`,
191
+ choices: [
192
+ {
193
+ name: "TypeScript",
194
+ value: "typescript",
195
+ description: "Static type checking for safer code",
196
+ disabled: manager === "bun" ? "Always enabled with bun" : false,
197
+ checked: manager === "bun" ? true : false
198
+ },
199
+ {
200
+ name: "Git",
201
+ value: "git",
202
+ description: "Initialize a git repository"
203
+ },
204
+ {
205
+ name: "ESLint",
206
+ value: "eslint",
207
+ description: "Code quality & consistency"
208
+ },
209
+ {
210
+ name: "Path Aliasing (@/*)",
211
+ value: "pathAliasing",
212
+ description: "Clean imports with @/ prefix"
213
+ }
214
+ ]
215
+ });
216
+ };
217
+
218
+ // src/scripts/common.ts
219
+ import { execSync } from "child_process";
220
+ import fs, { mkdirSync, existsSync, unlinkSync, writeFileSync, readFileSync } from "fs";
221
+ import path from "path";
222
+
223
+ // src/constants/common.ts
224
+ var gitignore = `
225
+ /node_modules
226
+ /.pnp
227
+ .pnp.*
228
+ .yarn/*
229
+ !.yarn/patches
230
+ !.yarn/plugins
231
+ !.yarn/releases
232
+ !.yarn/versions
233
+
234
+ # testing
235
+ /coverage
236
+
237
+ # next.js
238
+ /.next/
239
+ /out/
240
+
241
+ # production
242
+ /build
243
+ /dist
244
+ # misc
245
+ .DS_Store
246
+ *.pem
247
+
248
+ # debug
249
+ npm-debug.log*
250
+ yarn-debug.log*
251
+ yarn-error.log*
252
+ .pnpm-debug.log*
253
+
254
+ # env files (can opt-in for committing if needed)
255
+ .env.local
256
+ .env.development.local
257
+ .env.test.local
258
+ .env.production.local
259
+
260
+ # vercel
261
+ .vercel
262
+
263
+ # typescript
264
+ *.tsbuildinfo
265
+ next-env.d.ts
266
+
267
+ drizzle
268
+ `;
269
+
270
+ // src/scripts/common.ts
271
+ var initializeProject = (manager) => {
272
+ const command = manager === "pnpm" ? `${manager} init` : `${manager} init -y`;
273
+ execSync(command, {
274
+ stdio: "inherit"
275
+ });
276
+ if (manager !== "bun") {
277
+ execSync(`npm pkg set type=module`, {
278
+ stdio: "inherit"
279
+ });
280
+ }
281
+ };
282
+ var createRepo = (name) => {
283
+ if (!existsSync(name))
284
+ mkdirSync(name, { recursive: true });
285
+ };
286
+ var createRepoAndCd = (name) => {
287
+ process.chdir(path.resolve(name));
288
+ };
289
+ var changeDir = (name) => {
290
+ process.chdir(path.resolve(name));
291
+ };
292
+ var removeFile = (name) => {
293
+ if (existsSync(name)) {
294
+ unlinkSync(name);
295
+ }
296
+ };
297
+ var writeData = (name, data) => {
298
+ const dir = name.split("/").slice(0, -1).join("/");
299
+ if (dir) createRepo(dir);
300
+ writeFileSync(name, data);
301
+ };
302
+ var addPackage = (manager, packageName) => {
303
+ const command = manager === "npm" ? `npm i ${packageName}` : `${manager} add ${packageName}`;
304
+ execSync(command, {
305
+ stdio: "inherit"
306
+ });
307
+ };
308
+ var addDevPackage = (manager, packageName) => {
309
+ const command = manager === "npm" ? `npm i -D ${packageName}` : `${manager} add -D ${packageName}`;
310
+ execSync(command, {
311
+ stdio: "inherit"
312
+ });
313
+ };
314
+ var packageExecutor = (manager, pkg) => {
315
+ const exeMap = {
316
+ npm: "npx",
317
+ pnpm: "npx",
318
+ yarn: "npx",
319
+ bun: "bunx"
320
+ };
321
+ const executor = exeMap[manager];
322
+ execSync(`${executor} ${pkg}`, {
323
+ stdio: "inherit"
324
+ });
325
+ };
326
+ var moduleExecutor = (manager, command) => {
327
+ const exeMap = {
328
+ npm: "npx",
329
+ pnpm: "npx",
330
+ yarn: "npx",
331
+ bun: "bunx"
332
+ };
333
+ const executor = exeMap[manager];
334
+ execSync(`${executor} ${command}`, {
335
+ stdio: "inherit"
336
+ });
337
+ };
338
+ var addScripts = (scripts) => {
339
+ const path2 = "./package.json";
340
+ const pkg = JSON.parse(fs.readFileSync(path2, "utf-8"));
341
+ pkg.scripts = pkg.scripts || {};
342
+ scripts.forEach((prop) => {
343
+ pkg.scripts[prop.key] = prop.command;
344
+ });
345
+ writeData(path2, JSON.stringify(pkg, null, 2));
346
+ };
347
+ var initializeGit = (git) => {
348
+ if (git) {
349
+ writeData(".gitignore", gitignore);
350
+ execSync("git init", {
351
+ stdio: "inherit"
352
+ });
353
+ }
354
+ return;
355
+ };
356
+ var pathAliasingConfig = (filename, language) => {
357
+ let file;
358
+ if (language === "ts") {
359
+ file = JSON.parse(readFileSync(filename, "utf-8"));
360
+ } else {
361
+ file = {
362
+ compilerOptions: {}
363
+ };
364
+ }
365
+ const compiler = file.compilerOptions || {};
366
+ compiler.paths = compiler.paths || {};
367
+ const key = "@/*";
368
+ compiler.paths[key] = ["./src/*"];
369
+ file.compilerOptions = compiler;
370
+ writeData(filename, JSON.stringify(file, null, 2));
371
+ };
372
+
373
+ // src/scripts/express.ts
374
+ import { writeFile } from "fs/promises";
375
+
376
+ // src/constants/express.ts
377
+ var tsconfig = `{
378
+ "compilerOptions": {
379
+ "rootDir": "./src",
380
+ "outDir": "./dist",
381
+
382
+ "target": "ESNext",
383
+ "module": "NodeNext",
384
+
385
+ "strict": true,
386
+ "moduleDetection": "force",
387
+ "verbatimModuleSyntax": true,
388
+ "isolatedModules": true,
389
+
390
+ "sourceMap": true,
391
+ "declaration": true,
392
+ "declarationMap": true,
393
+
394
+ "skipLibCheck": true,
395
+ "types": []
396
+ },
397
+ "include": ["src"],
398
+ "exclude": ["dist", "node_modules"]
399
+ }`;
400
+ var expressBoilerplate = (language) => {
401
+ if (language === "ts") {
402
+ return `
403
+ import express, { type Request, type Response } from "express";
404
+
405
+ const app = express();
406
+
407
+ app.get("/", (_req: Request, res: Response) => {
408
+ res.send("Hello World!");
409
+ });
410
+
411
+ app.listen(3000, () => {
412
+ console.log("Example app listening on port 3000");
413
+ });
414
+ `;
415
+ }
416
+ return `
417
+ import express from "express";
418
+
419
+ const app = express();
420
+
421
+ app.get("/", (_req, res) => {
422
+ res.send("Hello World!");
423
+ });
424
+
425
+ app.listen(3000, () => {
426
+ console.log("Example app listening on port 3000");
427
+ });
428
+ `;
429
+ };
430
+ var eslint = (language) => {
431
+ if (language === "ts") {
432
+ return `
433
+ import js from "@eslint/js";
434
+ import globals from "globals";
435
+ import tseslint from "typescript-eslint";
436
+
437
+ export default [
438
+ js.configs.recommended,
439
+ ...tseslint.configs.recommended,
440
+
441
+ {
442
+ files: ["**/*.ts"],
443
+
444
+ languageOptions: {
445
+ parser: tseslint.parser,
446
+
447
+ parserOptions: {
448
+ project: "./tsconfig.json",
449
+ },
450
+
451
+ globals: {
452
+ ...globals.node,
453
+ },
454
+ },
455
+
456
+ rules: {
457
+ "@typescript-eslint/no-unused-vars": [
458
+ "warn",
459
+ {
460
+ argsIgnorePattern: "^_",
461
+ varsIgnorePattern: "^_",
462
+ },
463
+ ],
464
+
465
+ "@typescript-eslint/consistent-type-imports": "warn",
466
+
467
+ "no-console": "off",
468
+ "eqeqeq": ["error", "always"],
469
+ "curly": ["error", "all"],
470
+ "prefer-const": "warn",
471
+ },
472
+ },
473
+
474
+ {
475
+ ignores: [
476
+ "node_modules",
477
+ "dist",
478
+ "build",
479
+ "coverage",
480
+ ],
481
+ },
482
+ ];
483
+ `;
484
+ }
485
+ return `
486
+ import js from "@eslint/js";
487
+ import globals from "globals";
488
+
489
+ export default [
490
+ js.configs.recommended,
491
+
492
+ {
493
+ files: ["**/*.js"],
494
+
495
+ languageOptions: {
496
+ ecmaVersion: "latest",
497
+ sourceType: "module",
498
+
499
+ globals: {
500
+ ...globals.node,
501
+ },
502
+ },
503
+
504
+ rules: {
505
+ // code quality
506
+ "no-unused-vars": [
507
+ "warn",
508
+ {
509
+ argsIgnorePattern: "^_",
510
+ varsIgnorePattern: "^_",
511
+ },
512
+ ],
513
+
514
+ "no-console": "off",
515
+ "no-debugger": "warn",
516
+
517
+ // async/backend safety
518
+ "no-async-promise-executor": "error",
519
+ "require-await": "warn",
520
+
521
+ // style-ish
522
+ "object-shorthand": "warn",
523
+ "prefer-const": "warn",
524
+
525
+ // common backend mistakes
526
+ "eqeqeq": ["error", "always"],
527
+ "curly": ["error", "all"],
528
+
529
+ // imports
530
+ "no-duplicate-imports": "error",
531
+ },
532
+ },
533
+
534
+ {
535
+ ignores: [
536
+ "node_modules",
537
+ "dist",
538
+ "build",
539
+ "coverage",
540
+ ".env",
541
+ ],
542
+ },
543
+ ];
544
+ `;
545
+ };
546
+
547
+ // src/scripts/express.ts
548
+ var expressServer = async ({ manager, ts }) => {
549
+ initializeProject(manager);
550
+ removeFile("index.ts");
551
+ addPackage(manager, "express@5.2.1");
552
+ createRepo("src");
553
+ if (ts) {
554
+ addDevPackage(manager, "typescript@5.9.3 tsx@4.21.0 nodemon@3.1.9 @types/node@25.6.2 @types/express@5.0.6");
555
+ moduleExecutor(manager, "tsc --init");
556
+ await writeFile("src/index.ts", expressBoilerplate("ts"));
557
+ await writeFile("tsconfig.json", tsconfig);
558
+ addDevScripts(ts, manager === "bun" ? "bun" : "node");
559
+ } else {
560
+ await writeFile("src/index.js", expressBoilerplate("js"));
561
+ addDevScripts(ts, manager === "bun" ? "bun" : "node");
562
+ }
563
+ };
564
+ var addDevScripts = (ts, runtime) => {
565
+ const scripts = [];
566
+ if (ts === true) {
567
+ scripts.push(
568
+ {
569
+ key: "build",
570
+ command: "tsc"
571
+ },
572
+ {
573
+ key: "start",
574
+ command: `${runtime} dist/index.js`
575
+ },
576
+ {
577
+ key: "dev",
578
+ command: "tsx src/index.ts"
579
+ }
580
+ );
581
+ } else {
582
+ scripts.push(
583
+ {
584
+ key: "start",
585
+ command: `${runtime} src/index.js`
586
+ }
587
+ );
588
+ }
589
+ addScripts(scripts);
590
+ };
591
+ var addEslint = ({ eslint: eslint2, ts, manager }) => {
592
+ if (!eslint2) {
593
+ return;
594
+ }
595
+ if (ts) {
596
+ addDevPackage(manager, "eslint@9.26.0 @eslint/js@9.26.0 globals@16.0.0 typescript@5.9.3 typescript-eslint@8.59.2 jiti@2.7.0");
597
+ writeData("eslint.config.js", eslint("ts"));
598
+ } else {
599
+ addDevPackage(manager, "eslint@9.26.0 @eslint/js@9.26.0 globals@16.0.0");
600
+ writeData("eslint.config.js", eslint("js"));
601
+ }
602
+ const scripts = [];
603
+ scripts.push(
604
+ {
605
+ key: "lint",
606
+ command: "eslint ."
607
+ },
608
+ {
609
+ key: "lint:fix",
610
+ command: "eslint . --fix"
611
+ }
612
+ );
613
+ addScripts(scripts);
614
+ };
615
+ var expressPathAliasing = ({ ts, pathAliasing }) => {
616
+ if (!pathAliasing) {
617
+ return;
618
+ }
619
+ if (ts) {
620
+ pathAliasingConfig("tsconfig.json", "ts");
621
+ } else {
622
+ pathAliasingConfig("jsconfig.json", "js");
623
+ }
624
+ };
625
+
626
+ // src/setup/express.ts
627
+ var buildExpressApp = async (manager) => {
628
+ step(1, "Project name");
629
+ const name = await typeProjectName();
630
+ step(2, "Extra tools");
631
+ const answers = await expressPrompts(manager);
632
+ const ts = answers.includes("typescript");
633
+ const git = answers.includes("git");
634
+ const eslint2 = answers.includes("eslint");
635
+ const pathAliasing = answers.includes("pathAliasing");
636
+ step(3, "Creating project directory");
637
+ createRepoAndCd(name);
638
+ step(4, "Setting up Express server");
639
+ await expressServer({ manager, ts });
640
+ success("Express project created");
641
+ if (git) {
642
+ step(5, "Initializing Git repository");
643
+ initializeGit(git);
644
+ success("Git repository initialized");
645
+ }
646
+ if (eslint2) {
647
+ step(6, "Setting up ESLint");
648
+ addEslint({ eslint: eslint2, ts, manager });
649
+ success("ESLint configured");
650
+ }
651
+ if (pathAliasing) {
652
+ step(7, "Setting up path aliasing");
653
+ expressPathAliasing({ ts, pathAliasing });
654
+ success("Path aliasing configured");
655
+ }
656
+ done();
657
+ };
658
+
659
+ // src/prompts/nextjs.ts
660
+ import { checkbox as checkbox2, select as select3 } from "@inquirer/prompts";
661
+ var nextChoices = {
662
+ husky: { name: "Husky", description: "Git hooks made easy" },
663
+ shadcn: { name: "shadcn/ui", description: "Beautifully designed React components" },
664
+ trpc: { name: "tRPC", description: "End-to-end typesafe APIs" },
665
+ Drizzle: { name: "Drizzle ORM", description: "TypeScript ORM for SQL" },
666
+ reactCompiler: { name: "React Compiler", description: "Optimize React rendering" },
667
+ betterAuth: { name: "Better Auth", description: "Authentication framework (auto-includes Drizzle)" }
668
+ };
669
+ var nextjsChoices = async () => {
670
+ const answers = await checkbox2({
671
+ message: `${colors.cyan("\u276F")} Extra tools to include`,
672
+ choices: Object.entries(nextChoices).map(
673
+ ([value, meta]) => ({
674
+ name: meta.name,
675
+ value,
676
+ description: meta.description
677
+ })
678
+ )
679
+ });
680
+ const selected = new Set(answers);
681
+ if (selected.has("betterAuth")) {
682
+ selected.add("Drizzle");
683
+ }
684
+ return Array.from(selected);
685
+ };
686
+ var linter = async () => {
687
+ return await select3({
688
+ message: `${colors.cyan("\u276F")} Pick a linter`,
689
+ choices: [
690
+ { name: "ESLint", value: "eslint", description: "Standard linter with rich ecosystem" },
691
+ { name: "Biome", value: "biome", description: "Fast, modern linter & formatter" },
692
+ { name: "None", value: "None", description: "Skip linter setup" }
693
+ ]
694
+ });
695
+ };
696
+
697
+ // src/scripts/nextjs.ts
698
+ import { execSync as execSync2 } from "child_process";
699
+ import { appendFileSync, readFileSync as readFileSync2 } from "fs";
700
+ var scaffoldCommandMap = {
701
+ npm: "npx create-next-app@latest",
702
+ pnpm: "pnpm create next-app",
703
+ yarn: "yarn create next-app",
704
+ bun: "bunx create-next-app"
705
+ };
706
+ var buildFlags = (answer, linter2) => {
707
+ const flagsArray = ["--tailwind", "--ts", "--src-dir", "--app", "--no-agents-md", "--skip-install", `--import-alias "@/*"`];
708
+ if (answer.includes("reactCompiler")) flagsArray.push("--react-compiler");
709
+ else flagsArray.push("--no-react-compiler");
710
+ switch (linter2) {
711
+ case "biome":
712
+ flagsArray.push("--biome");
713
+ break;
714
+ case "eslint":
715
+ flagsArray.push("--eslint");
716
+ break;
717
+ default:
718
+ flagsArray.push("--no-eslint");
719
+ }
720
+ return flagsArray;
721
+ };
722
+ var installNextApp = (manager, flagsArray, name) => {
723
+ if (manager === "pnpm")
724
+ flagsArray.push("--skip-install");
725
+ const flagString = flagsArray.join(" ");
726
+ const scaffoldCommand = `${scaffoldCommandMap[manager]} ${name} ${flagString} `;
727
+ execSync2(scaffoldCommand, {
728
+ stdio: "inherit"
729
+ });
730
+ changeDir(name);
731
+ if (manager === "pnpm") {
732
+ execSync2("pnpm install --config.ignore-scripts=false --no-frozen-lockfile", {
733
+ stdio: "inherit"
734
+ });
735
+ }
736
+ };
737
+ var shadcnInstall = (manager, shadcn) => {
738
+ if (!shadcn) return;
739
+ packageExecutor(manager, "shadcn@latest init --preset b0 --template next");
740
+ packageExecutor(manager, "shadcn@latest add --all");
741
+ };
742
+ var trpcSetup = (manager, trpc) => {
743
+ if (!trpc) return;
744
+ addPackage(
745
+ manager,
746
+ "@trpc/server@11.17.0 @trpc/client@11.17.0 @trpc/react-query@11.17.0 @trpc/next@11.17.0 @tanstack/react-query@5.100.10 superjson@2.2.5 zod@3.24.3"
747
+ );
748
+ createRepo("src/server/routers");
749
+ writeData(
750
+ "src/server/trpc.ts",
751
+ `import { initTRPC } from "@trpc/server"
752
+ import superjson from "superjson"
753
+ import { ZodError } from "zod"
754
+
755
+ const t = initTRPC.create({
756
+ transformer: superjson,
757
+ errorFormatter({ shape, error }) {
758
+ return {
759
+ ...shape,
760
+ data: {
761
+ ...shape.data,
762
+ zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
763
+ },
764
+ }
765
+ },
766
+ })
767
+
768
+ export const createTRPCRouter = t.router
769
+ export const publicProcedure = t.procedure`
770
+ );
771
+ writeData(
772
+ "src/server/routers/_app.ts",
773
+ `import { createTRPCRouter } from "@/server/trpc"
774
+
775
+ export const appRouter = createTRPCRouter({})
776
+
777
+ export type AppRouter = typeof appRouter`
778
+ );
779
+ createRepo("src/trpc");
780
+ writeData(
781
+ "src/trpc/react.tsx",
782
+ `"use client"
783
+
784
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
785
+ import { loggerLink, unstable_httpBatchStreamLink } from "@trpc/client"
786
+ import { createTRPCReact } from "@trpc/react-query"
787
+ import { useState } from "react"
788
+ import type { AppRouter } from "@/server/routers/_app"
789
+ import superjson from "superjson"
790
+
791
+ export const trpc = createTRPCReact<AppRouter>()
792
+
793
+ function getBaseUrl() {
794
+ if (typeof window !== "undefined") return ""
795
+ if (process.env.VERCEL_URL) return "https://" + process.env.VERCEL_URL
796
+ return "http://localhost:" + (process.env.PORT ?? 3000)
797
+ }
798
+
799
+ export function TRPCReactProvider(props: { children: React.ReactNode }) {
800
+ const [queryClient] = useState(() => new QueryClient())
801
+ const [trpcClient] = useState(() =>
802
+ trpc.createClient({
803
+ links: [
804
+ loggerLink({
805
+ enabled: (op) =>
806
+ process.env.NODE_ENV === "development" ||
807
+ (op.direction === "down" && op.result instanceof Error),
808
+ }),
809
+ unstable_httpBatchStreamLink({
810
+ transformer: superjson,
811
+ url: getBaseUrl() + "/api/trpc",
812
+ }),
813
+ ],
814
+ })
815
+ )
816
+
817
+ return (
818
+ <QueryClientProvider client={queryClient}>
819
+ <trpc.Provider client={trpcClient} queryClient={queryClient}>
820
+ {props.children}
821
+ </trpc.Provider>
822
+ </QueryClientProvider>
823
+ )
824
+ }`
825
+ );
826
+ writeData(
827
+ "src/trpc/server.ts",
828
+ `import { createTRPCClient, httpBatchLink } from "@trpc/client"
829
+ import type { AppRouter } from "@/server/routers/_app"
830
+ import superjson from "superjson"
831
+
832
+ export const trpc = createTRPCClient<AppRouter>({
833
+ links: [
834
+ httpBatchLink({
835
+ url: "/api/trpc",
836
+ transformer: superjson,
837
+ }),
838
+ ],
839
+ })`
840
+ );
841
+ createRepo("src/app/api/trpc/[trpc]");
842
+ writeData(
843
+ "src/app/api/trpc/[trpc]/route.ts",
844
+ `import { fetchRequestHandler } from "@trpc/server/adapters/fetch"
845
+ import { appRouter } from "@/server/routers/_app"
846
+
847
+ const handler = (req: Request) =>
848
+ fetchRequestHandler({
849
+ endpoint: "/api/trpc",
850
+ req,
851
+ router: appRouter,
852
+ })
853
+
854
+ export { handler as GET, handler as POST }`
855
+ );
856
+ const layoutPath = "src/app/layout.tsx";
857
+ const layout = readFileSync2(layoutPath, "utf-8");
858
+ const updated = layout.replace(
859
+ 'import "./globals.css"',
860
+ 'import "./globals.css"\nimport { TRPCReactProvider } from "@/trpc/react"'
861
+ ).replace(
862
+ /\{children\}(\s*)(<\/body>)/,
863
+ "<TRPCReactProvider>{children}</TRPCReactProvider>$1$2"
864
+ );
865
+ writeData(layoutPath, updated);
866
+ };
867
+ var drizzleSetup = (manager, drizzle) => {
868
+ if (!drizzle) return;
869
+ addPackage(manager, "drizzle-orm@0.45.2 pg@9.6.0");
870
+ addDevPackage(manager, "drizzle-kit@0.31.10 tsx@4.21.0 @types/pg@latest");
871
+ appendFileSync(".env", "DATABASE_URL=\n");
872
+ writeData(
873
+ "drizzle.config.ts",
874
+ `import { defineConfig } from "drizzle-kit"
875
+ export default defineConfig({
876
+ schema: "./src/db/schema",
877
+ out: "./drizzle",
878
+ dialect: "postgresql",
879
+ dbCredentials: { url: process.env.DATABASE_URL | "" },
880
+ })`
881
+ );
882
+ createRepo("src/db");
883
+ writeData(
884
+ "src/db/schema/user.ts",
885
+ `import { integer, pgTable, varchar } from "drizzle-orm/pg-core";
886
+ export const usersTable = pgTable("users", {
887
+ id: integer().primaryKey().generatedAlwaysAsIdentity(),
888
+ name: varchar().notNull(),
889
+ age: integer().notNull(),
890
+ email: varchar().notNull().unique(),
891
+ });`
892
+ );
893
+ writeData(
894
+ "src/config/db.ts",
895
+ `import { drizzle } from "drizzle-orm/node-postgres";
896
+ import { Pool } from "pg";
897
+ const pool = new Pool({
898
+ connectionString: process.env.DATABASE_URL!,
899
+ });
900
+ export const db = drizzle({ client: pool });`
901
+ );
902
+ addScripts([
903
+ {
904
+ key: "db:generate",
905
+ command: "drizzle-kit generate"
906
+ },
907
+ {
908
+ key: "db:migrate",
909
+ command: "drizzle-kit migrate"
910
+ },
911
+ {
912
+ key: "db:push",
913
+ command: "drizzle-kit push"
914
+ }
915
+ ]);
916
+ };
917
+ var huskySetup = (manager, husky) => {
918
+ if (!husky) return;
919
+ addDevPackage(manager, "husky@9.1.7");
920
+ packageExecutor(manager, "husky init");
921
+ };
922
+ var authSetup = (manager, auth) => {
923
+ if (!auth) return;
924
+ createRepo("src/lib");
925
+ addPackage(manager, "better-auth@1.6.9");
926
+ appendFileSync(".env", "BETTER_AUTH_URL=http://localhost:3000\nBETTER_AUTH_SECRET=0zNTvTuWUIH0rBOSbVTofnA6Ojq4zcHCl+INhkkiGy8=\n");
927
+ const authContent = `import { betterAuth } from "better-auth";
928
+ import { drizzleAdapter } from "better-auth/adapters/drizzle";
929
+ import { db } from "@/config/db";
930
+ export const auth = betterAuth({
931
+ database: drizzleAdapter(db, {
932
+ provider: "pg",
933
+ }),
934
+ emailAndPassword: {
935
+ enabled: true,
936
+ }, socialProviders: {
937
+ github: {
938
+ clientId: process.env.GITHUB_CLIENT_ID as string,
939
+ clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
940
+ },
941
+ },
942
+ });`;
943
+ writeData("src/lib/auth.ts", authContent);
944
+ removeFile("src/db/schema/user.ts");
945
+ packageExecutor(manager, `auth@latest generate --output "src/db/schema/user.ts" --yes`);
946
+ createRepo("src/app/api/auth/[...all]");
947
+ writeData(
948
+ "src/app/api/auth/[...all]/route.ts",
949
+ `import { auth } from "@/lib/auth"
950
+ import { toNextJsHandler } from "better-auth/next-js"
951
+ export const { POST, GET } = toNextJsHandler(auth)`
952
+ );
953
+ writeData("src/lib/auth-client.ts", `
954
+ import { createAuthClient } from "better-auth/react"
955
+ export const authClient = createAuthClient({
956
+ /** The base URL of the server (optional if you're using the same domain) */
957
+ baseURL: "http://localhost:3000"
958
+ })
959
+ `);
960
+ };
961
+
962
+ // src/setup/nextjs.ts
963
+ var buildNextApp = async (manager) => {
964
+ step(1, "Project name");
965
+ const name = await typeProjectName();
966
+ step(2, "Extra tools");
967
+ const choices = await nextjsChoices();
968
+ step(3, "Linter");
969
+ const linterChoice = await linter();
970
+ const trpc = choices.includes("trpc");
971
+ const shadcn = choices.includes("shadcn");
972
+ const drizzle = choices.includes("Drizzle");
973
+ const husky = choices.includes("husky");
974
+ const auth = choices.includes("betterAuth");
975
+ step(4, "Scaffolding Next.js project");
976
+ const flags = buildFlags(choices, linterChoice);
977
+ installNextApp(manager, flags, name);
978
+ success("Next.js project created");
979
+ if (shadcn) {
980
+ step(5, "Installing shadcn/ui");
981
+ shadcnInstall(manager, shadcn);
982
+ success("shadcn/ui installed");
983
+ }
984
+ if (trpc) {
985
+ step(6, "Setting up tRPC");
986
+ trpcSetup(manager, trpc);
987
+ success("tRPC configured");
988
+ }
989
+ if (drizzle) {
990
+ step(7, "Setting up Drizzle ORM");
991
+ drizzleSetup(manager, drizzle);
992
+ success("Drizzle ORM configured");
993
+ }
994
+ if (husky) {
995
+ step(8, "Setting up Husky");
996
+ huskySetup(manager, husky);
997
+ success("Husky configured");
998
+ }
999
+ if (auth) {
1000
+ step(9, "Setting up Better Auth");
1001
+ authSetup(manager, auth);
1002
+ success("Better Auth configured");
1003
+ }
1004
+ done();
1005
+ };
1006
+
1007
+ // src/prompts/vite.ts
1008
+ import { checkbox as checkbox3 } from "@inquirer/prompts";
1009
+ var viteChoices = async () => {
1010
+ const answers = await checkbox3({
1011
+ message: `${colors.cyan("\u276F")} Extra tools to include`,
1012
+ choices: [
1013
+ {
1014
+ name: "TypeScript",
1015
+ value: "typescript",
1016
+ description: "Static type checking"
1017
+ },
1018
+ {
1019
+ name: "Path Aliasing (@/ imports)",
1020
+ value: "pathAlising",
1021
+ description: "Clean imports with @/ prefix (required for shadcn)"
1022
+ },
1023
+ {
1024
+ name: "React Compiler",
1025
+ value: "reactCompiler",
1026
+ description: "Optimize React rendering automatically"
1027
+ },
1028
+ {
1029
+ name: "Tailwind CSS",
1030
+ value: "tailwind",
1031
+ description: "Utility-first CSS framework"
1032
+ },
1033
+ {
1034
+ name: "shadcn/ui",
1035
+ value: "shadcn",
1036
+ description: "Beautiful React components (auto-includes TS + Tailwind)"
1037
+ },
1038
+ {
1039
+ name: "React Router",
1040
+ value: "reactRouter",
1041
+ description: "Client-side routing for SPAs"
1042
+ }
1043
+ ]
1044
+ });
1045
+ const selected = new Set(answers);
1046
+ if (selected.has("shadcn")) {
1047
+ selected.add("typescript");
1048
+ selected.add("tailwind");
1049
+ }
1050
+ return Array.from(selected);
1051
+ };
1052
+
1053
+ // src/scripts/vite.ts
1054
+ import { execSync as execSync3 } from "child_process";
1055
+ import { readFileSync as readFileSync3 } from "fs";
1056
+
1057
+ // src/constants/vite.ts
1058
+ var reactRouterEntry = (language) => {
1059
+ const appImport = language === "ts" ? `import App from "./App";` : `import App from "./App";`;
1060
+ return `
1061
+ import ReactDOM from "react-dom/client";
1062
+ import { createBrowserRouter, RouterProvider } from "react-router";
1063
+ ${appImport}
1064
+ const router = createBrowserRouter([
1065
+ {
1066
+ path: "/",
1067
+ Component: App,
1068
+ },
1069
+ ]);
1070
+
1071
+ const root = document.getElementById("root");
1072
+
1073
+ if (!root) {
1074
+ throw new Error("Root element not found");
1075
+ }
1076
+
1077
+ ReactDOM.createRoot(root).render(<RouterProvider router={router} />);
1078
+ `;
1079
+ };
1080
+ var viteAppTsconfig = () => {
1081
+ return `
1082
+ {
1083
+ "compilerOptions": {
1084
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
1085
+ "target": "es2023",
1086
+ "lib": ["ES2023", "DOM"],
1087
+ "module": "esnext",
1088
+ "types": ["vite/client"],
1089
+ "skipLibCheck": true,
1090
+
1091
+ "moduleResolution": "bundler",
1092
+ "allowImportingTsExtensions": true,
1093
+ "verbatimModuleSyntax": true,
1094
+ "moduleDetection": "force",
1095
+ "noEmit": true,
1096
+ "jsx": "react-jsx",
1097
+ "paths": {
1098
+ "@/*": ["./src/*"]
1099
+ },
1100
+ "noUnusedLocals": true,
1101
+ "noUnusedParameters": true,
1102
+ "erasableSyntaxOnly": true,
1103
+ "noFallthroughCasesInSwitch": true
1104
+ },
1105
+ "include": ["src"]
1106
+ }
1107
+
1108
+ `;
1109
+ };
1110
+
1111
+ // src/scripts/vite.ts
1112
+ var initiateVite = ({ manager, ts, name }) => {
1113
+ const template = ts ? "react-ts" : "react";
1114
+ const command = `${manager} create vite ${name} --template ${template} --no-interactive`;
1115
+ execSync3(command, {
1116
+ stdio: "inherit"
1117
+ });
1118
+ changeDir(name);
1119
+ };
1120
+ var viteConfigCreate = ({ tailwind, rc, pathAliasing }) => {
1121
+ const imports = [`import { defineConfig } from "vite";`];
1122
+ if (rc) {
1123
+ imports.push(`import react, { reactCompilerPreset } from "@vitejs/plugin-react";`);
1124
+ imports.push(`import rolldownBabel from "@rolldown/plugin-babel";`);
1125
+ } else {
1126
+ imports.push(`import react from "@vitejs/plugin-react";`);
1127
+ }
1128
+ const plugins = [`react()`];
1129
+ if (tailwind) {
1130
+ imports.push(`import tailwindcss from "@tailwindcss/vite";`);
1131
+ plugins.push(`tailwindcss()`);
1132
+ }
1133
+ if (pathAliasing) {
1134
+ imports.push(`import path from "path";`);
1135
+ }
1136
+ if (rc) {
1137
+ plugins.push(`rolldownBabel({
1138
+ presets: [reactCompilerPreset()],
1139
+ })`);
1140
+ }
1141
+ const fileCode = `
1142
+ ${imports.join("\n")}
1143
+
1144
+ export default defineConfig({
1145
+ plugins: [
1146
+ ${plugins.join(",\n ")},
1147
+ ],
1148
+ ${pathAliasing ? `
1149
+ resolve: {
1150
+ alias: {
1151
+ "@": path.resolve(__dirname, "./src"),
1152
+ },
1153
+ },
1154
+ ` : ""}
1155
+ });
1156
+ `;
1157
+ return fileCode;
1158
+ };
1159
+ var rewriteViteConfig = (data, ts) => {
1160
+ const fileName = ts ? "vite.config.ts" : "vite.config.js";
1161
+ writeData(fileName, data);
1162
+ };
1163
+ var patchViteEslintConfigForShadcn = (shadcn) => {
1164
+ if (!shadcn) {
1165
+ return;
1166
+ }
1167
+ const fileName = "eslint.config.js";
1168
+ const file = readFileSync3(fileName, "utf-8");
1169
+ if (file.includes("react-refresh/only-export-components")) {
1170
+ return;
1171
+ }
1172
+ const overrideBlock = `,
1173
+ {
1174
+ files: ["src/components/ui/**/*.tsx", "src/hooks/**/*.ts"],
1175
+ rules: {
1176
+ "react-refresh/only-export-components": "off",
1177
+ "react-hooks/set-state-in-effect": "off",
1178
+ },
1179
+ }`;
1180
+ const updatedFile = file.replace(/\]\s*$/, `${overrideBlock}
1181
+ ]`);
1182
+ writeData(fileName, updatedFile);
1183
+ };
1184
+ var tailwindViteSetup = (manager, tailwind) => {
1185
+ if (!tailwind) {
1186
+ return;
1187
+ }
1188
+ addPackage(manager, "tailwindcss@4.3.0 @tailwindcss/vite@4.2.4");
1189
+ writeData("src/App.css", `@import "tailwindcss";
1190
+ `);
1191
+ };
1192
+ var reactCompilerSetup = (manager, rc) => {
1193
+ if (!rc) {
1194
+ return;
1195
+ }
1196
+ addDevPackage(manager, "@rolldown/plugin-babel@0.2.0 @babel/core@7.27.1 babel-plugin-react-compiler@19.1.0 @types/babel__core@7.20.5");
1197
+ };
1198
+ var shadcnSetup = (shadcn, manager) => {
1199
+ if (!shadcn) {
1200
+ return;
1201
+ }
1202
+ const initCommand = `${manager === "bun" ? "--bun " : ""}shadcn@latest init -d`;
1203
+ const addCommand = `${manager === "bun" ? "--bun " : ""}shadcn@latest add --all`;
1204
+ packageExecutor(manager, initCommand);
1205
+ packageExecutor(manager, addCommand);
1206
+ };
1207
+ var reactRouterSetup = ({ rr, manager, ts }) => {
1208
+ if (!rr) {
1209
+ return;
1210
+ }
1211
+ addPackage(manager, "react-router@7.12.0");
1212
+ if (ts) {
1213
+ writeData("src/main.tsx", reactRouterEntry("ts"));
1214
+ } else {
1215
+ writeData("src/main.jsx", reactRouterEntry("js"));
1216
+ }
1217
+ };
1218
+ var pathAliasingSetup = ({ pathAliasing, ts }) => {
1219
+ if (!pathAliasing) {
1220
+ return;
1221
+ }
1222
+ const language = ts ? "ts" : "js";
1223
+ if (language === "ts") {
1224
+ pathAliasingConfig("tsconfig.json", language);
1225
+ writeData("tsconfig.app.json", viteAppTsconfig());
1226
+ } else {
1227
+ pathAliasingConfig("jsconfig.json", language);
1228
+ }
1229
+ };
1230
+
1231
+ // src/setup/vite.ts
1232
+ var buildViteApp = async (manager) => {
1233
+ step(1, "Project name");
1234
+ const name = await typeProjectName();
1235
+ step(2, "Extra tools");
1236
+ const answers = await viteChoices();
1237
+ const ts = answers.includes("typescript");
1238
+ const tailwind = answers.includes("tailwind");
1239
+ const rc = answers.includes("reactCompiler");
1240
+ const rr = answers.includes("reactRouter");
1241
+ const shadcn = answers.includes("shadcn");
1242
+ const pathAliasing = answers.includes("pathAlising");
1243
+ step(3, "Scaffolding Vite project");
1244
+ initiateVite({ ts, manager, name });
1245
+ success("Vite project created");
1246
+ const viteConfig = viteConfigCreate({ tailwind, rc, pathAliasing });
1247
+ rewriteViteConfig(viteConfig, ts);
1248
+ pathAliasingSetup({ pathAliasing, ts });
1249
+ patchViteEslintConfigForShadcn(shadcn);
1250
+ if (tailwind) {
1251
+ step(4, "Setting up Tailwind CSS");
1252
+ tailwindViteSetup(manager, tailwind);
1253
+ success("Tailwind CSS configured");
1254
+ }
1255
+ if (rc) {
1256
+ step(5, "Setting up React Compiler");
1257
+ reactCompilerSetup(manager, rc);
1258
+ success("React Compiler configured");
1259
+ }
1260
+ if (shadcn) {
1261
+ step(6, "Installing shadcn/ui");
1262
+ shadcnSetup(shadcn, manager);
1263
+ success("shadcn/ui installed");
1264
+ }
1265
+ if (rr) {
1266
+ step(7, "Setting up React Router");
1267
+ reactRouterSetup({ rr, manager, ts });
1268
+ success("React Router configured");
1269
+ }
1270
+ done();
1271
+ };
1272
+
1273
+ // src/utils/frameworkMapper.ts
1274
+ var mapper = {
1275
+ nextjs: buildNextApp,
1276
+ vite: buildViteApp,
1277
+ express: buildExpressApp,
1278
+ electron: buildElectronApp,
1279
+ expo: buildExpoApp
1280
+ };
1281
+
1282
+ // src/index.ts
1283
+ var program = new Command();
1284
+ program.name("Rebar").description("Interactive CLI for scaffolding JavaScript and TypeScript projects across multiple frameworks.").version("1.0.0");
1285
+ program.command("init").description("choose a template to start").action(async () => {
1286
+ banner();
1287
+ info(`Let's scaffold your project! Answer a few questions to get started.
1288
+ `);
1289
+ const manager = await choosePackageManager();
1290
+ const fw = await chooseFramework(manager);
1291
+ const func = mapper[fw];
1292
+ if (!func) {
1293
+ process.exit(1);
1294
+ }
1295
+ func(manager);
1296
+ });
1297
+ program.command("start").description("Enter template parameters").option("-m ,--packageManager <manager>", "package Manager: npm, bun, yarn").option("-f ,--framework <framework>", "framework: nextjs,vite,express,expo,electron").action((options) => {
1298
+ banner();
1299
+ const { packageManager, framework } = options;
1300
+ if (!packageManager || !framework) {
1301
+ error(`Missing required options. Use --packageManager and --framework.`);
1302
+ console.log(colors.gray(` Example: rebar start -m npm -f vite`));
1303
+ process.exit(1);
1304
+ }
1305
+ if (packageManager === "pnpm") {
1306
+ error(`pnpm is not supported (pnpm install --ignore-scripts breaks builds).`);
1307
+ console.log(colors.gray(` Use npm, yarn, or bun instead.`));
1308
+ process.exit(1);
1309
+ }
1310
+ info(`Scaffolding a ${colors.bold(framework)} project with ${colors.bold(packageManager)}...
1311
+ `);
1312
+ const func = mapper[framework];
1313
+ if (!func) {
1314
+ error(`Invalid framework "${framework}". Run ${colors.bold("rebar --help")} for options.`);
1315
+ process.exit(1);
1316
+ }
1317
+ func(packageManager);
1318
+ });
1319
+ program.parse();