@shakil-dev/shakil-stack 2.2.5 → 2.2.7
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 +57 -46
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as
|
|
2
|
+
import{Command as Te}from"commander";import pe from"fs-extra";import N from"path";import{fileURLToPath as we}from"url";import s from"fs-extra";import r from"path";import n from"chalk";import ue from"ora";import K from"inquirer";import{execSync as me}from"child_process";import Se from"fs-extra";var m=(e,t=process.cwd())=>{try{return me(e,{stdio:"inherit",cwd:t}),!0}catch{return!1}},w=()=>{let e=process.env.npm_config_user_agent||"";return e.includes("pnpm")?"pnpm":e.includes("yarn")?"yarn":"npm"};var $=e=>`import { Server } from 'http';
|
|
3
3
|
import app from './app.js';
|
|
4
4
|
import config from './app/config/index.js';
|
|
5
5
|
|
|
@@ -14,7 +14,7 @@ async function bootstrap() {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
bootstrap();
|
|
17
|
-
`,
|
|
17
|
+
`,_=e=>`import cors from 'cors';
|
|
18
18
|
import express, { Application, Request, Response } from 'express';
|
|
19
19
|
import httpStatus from 'http-status';
|
|
20
20
|
import globalErrorHandler from './app/middleware/globalErrorHandler.js';
|
|
@@ -25,12 +25,13 @@ import morgan from 'morgan';
|
|
|
25
25
|
import helmet from 'helmet';
|
|
26
26
|
import { rateLimit } from 'express-rate-limit';
|
|
27
27
|
import { sanitizeRequest } from './app/middleware/sanitizeRequest.js';
|
|
28
|
+
import config from './app/config/index.js';
|
|
28
29
|
|
|
29
30
|
const app: Application = express();
|
|
30
31
|
|
|
31
32
|
app.use(helmet());
|
|
32
33
|
app.use(cors({
|
|
33
|
-
origin: ["http://localhost:3000", "http://127.0.0.1:3000"],
|
|
34
|
+
origin: [config.client_url as string, "http://localhost:3000", "http://127.0.0.1:3000"],
|
|
34
35
|
credentials: true,
|
|
35
36
|
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
|
|
36
37
|
allowedHeaders: ["Content-Type", "Authorization", "Cookie"]
|
|
@@ -65,7 +66,7 @@ app.use(globalErrorHandler);
|
|
|
65
66
|
app.use(notFound);
|
|
66
67
|
|
|
67
68
|
export default app;
|
|
68
|
-
`,
|
|
69
|
+
`,j=`import dotenv from 'dotenv';
|
|
69
70
|
import path from 'path';
|
|
70
71
|
|
|
71
72
|
dotenv.config({ path: path.join(process.cwd(), "..", ".env") });
|
|
@@ -75,8 +76,10 @@ export default {
|
|
|
75
76
|
port: process.env.PORT || 8000,
|
|
76
77
|
database_url: process.env.DATABASE_URL,
|
|
77
78
|
jwt_secret: process.env.JWT_SECRET,
|
|
79
|
+
better_auth_base_url: process.env.BETTER_AUTH_BASE_URL,
|
|
80
|
+
client_url: process.env.CLIENT_URL,
|
|
78
81
|
};
|
|
79
|
-
`,
|
|
82
|
+
`,v=`import "dotenv/config";
|
|
80
83
|
import { PrismaClient } from "@prisma/client";
|
|
81
84
|
import pkg from 'pg';
|
|
82
85
|
import { PrismaPg } from '@prisma/adapter-pg';
|
|
@@ -90,7 +93,7 @@ const prisma = new PrismaClient({ adapter });
|
|
|
90
93
|
|
|
91
94
|
export default prisma;
|
|
92
95
|
export { prisma };
|
|
93
|
-
`,
|
|
96
|
+
`,k=`import { betterAuth } from "better-auth";
|
|
94
97
|
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
95
98
|
import config from "../config/index.js";
|
|
96
99
|
import { prisma } from "./prisma.js";
|
|
@@ -100,16 +103,16 @@ export const auth = betterAuth({
|
|
|
100
103
|
provider: "postgresql",
|
|
101
104
|
}),
|
|
102
105
|
secret: config.jwt_secret,
|
|
103
|
-
baseURL:
|
|
104
|
-
trustedOrigins: [
|
|
106
|
+
baseURL: config.better_auth_base_url,
|
|
107
|
+
trustedOrigins: [config.client_url as string],
|
|
105
108
|
emailAndPassword: {
|
|
106
109
|
enabled: true,
|
|
107
110
|
},
|
|
108
111
|
});
|
|
109
|
-
`,
|
|
112
|
+
`,q=`import { Router } from 'express';
|
|
110
113
|
const router = Router();
|
|
111
114
|
export default router;
|
|
112
|
-
`,
|
|
115
|
+
`,L=`import { ErrorRequestHandler } from 'express';
|
|
113
116
|
import config from '../config/index.js';
|
|
114
117
|
|
|
115
118
|
const globalErrorHandler: ErrorRequestHandler = (error, req, res, next) => {
|
|
@@ -121,7 +124,7 @@ const globalErrorHandler: ErrorRequestHandler = (error, req, res, next) => {
|
|
|
121
124
|
};
|
|
122
125
|
|
|
123
126
|
export default globalErrorHandler;
|
|
124
|
-
`,
|
|
127
|
+
`,U=`import { Request, Response, NextFunction } from 'express';
|
|
125
128
|
import httpStatus from 'http-status';
|
|
126
129
|
|
|
127
130
|
const notFound = (req: Request, res: Response, next: NextFunction) => {
|
|
@@ -132,7 +135,7 @@ const notFound = (req: Request, res: Response, next: NextFunction) => {
|
|
|
132
135
|
};
|
|
133
136
|
|
|
134
137
|
export default notFound;
|
|
135
|
-
`,
|
|
138
|
+
`,H=`import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
136
139
|
|
|
137
140
|
const catchAsync = (fn: RequestHandler) => {
|
|
138
141
|
return async (req: Request, res: Response, next: NextFunction) => {
|
|
@@ -145,7 +148,7 @@ const catchAsync = (fn: RequestHandler) => {
|
|
|
145
148
|
};
|
|
146
149
|
|
|
147
150
|
export default catchAsync;
|
|
148
|
-
`,
|
|
151
|
+
`,M=`class ApiError extends Error {
|
|
149
152
|
statusCode: number;
|
|
150
153
|
constructor(statusCode: number, message: string | undefined, stack = '') {
|
|
151
154
|
super(message);
|
|
@@ -155,7 +158,7 @@ export default catchAsync;
|
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
export default ApiError;
|
|
158
|
-
`,
|
|
161
|
+
`,z=`import { JSDOM } from 'jsdom';
|
|
159
162
|
import createDOMPurify from 'dompurify';
|
|
160
163
|
|
|
161
164
|
const window = new JSDOM('').window;
|
|
@@ -170,7 +173,7 @@ export const sanitize = (data: any): any => {
|
|
|
170
173
|
}
|
|
171
174
|
return data;
|
|
172
175
|
};
|
|
173
|
-
`,
|
|
176
|
+
`,B=`import { Request, Response, NextFunction } from 'express';
|
|
174
177
|
import { sanitize } from '../utils/sanitizer.js';
|
|
175
178
|
|
|
176
179
|
export const sanitizeRequest = (req: Request, res: Response, next: NextFunction) => {
|
|
@@ -179,7 +182,7 @@ export const sanitizeRequest = (req: Request, res: Response, next: NextFunction)
|
|
|
179
182
|
if (req.params) sanitize(req.params);
|
|
180
183
|
next();
|
|
181
184
|
};
|
|
182
|
-
`,
|
|
185
|
+
`,W=`import { Response } from 'express';
|
|
183
186
|
|
|
184
187
|
type IResponse<T> = {
|
|
185
188
|
statusCode: number;
|
|
@@ -203,16 +206,16 @@ const sendResponse = <T>(res: Response, data: IResponse<T>) => {
|
|
|
203
206
|
};
|
|
204
207
|
|
|
205
208
|
export default sendResponse;
|
|
206
|
-
|
|
209
|
+
`,E=`generator client {
|
|
207
210
|
provider = "prisma-client-js"
|
|
208
|
-
previewFeatures = ["prismaSchemaFolder"]
|
|
209
211
|
output = "../../generated/prisma"
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
datasource db {
|
|
213
215
|
provider = "postgresql"
|
|
216
|
+
url = env("DATABASE_URL")
|
|
214
217
|
}
|
|
215
|
-
`,
|
|
218
|
+
`,G=`model User {
|
|
216
219
|
id String @id @default(uuid())
|
|
217
220
|
email String @unique
|
|
218
221
|
name String
|
|
@@ -248,7 +251,7 @@ model Account {
|
|
|
248
251
|
updatedAt DateTime @updatedAt
|
|
249
252
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
250
253
|
}
|
|
251
|
-
`,
|
|
254
|
+
`,R=`import "dotenv/config";
|
|
252
255
|
import { defineConfig } from "prisma/config";
|
|
253
256
|
import process from "process";
|
|
254
257
|
|
|
@@ -258,7 +261,7 @@ export default defineConfig({
|
|
|
258
261
|
url: process.env.DATABASE_URL,
|
|
259
262
|
},
|
|
260
263
|
});
|
|
261
|
-
`,
|
|
264
|
+
`,V=`{
|
|
262
265
|
"compilerOptions": {
|
|
263
266
|
"target": "ES2022",
|
|
264
267
|
"module": "NodeNext",
|
|
@@ -277,7 +280,7 @@ export default defineConfig({
|
|
|
277
280
|
"include": ["src/**/*"],
|
|
278
281
|
"exclude": ["node_modules", "dist"]
|
|
279
282
|
}
|
|
280
|
-
`;var
|
|
283
|
+
`;var S=`MIT License
|
|
281
284
|
|
|
282
285
|
Copyright (c) 2026 Shakil Ahmed Billal
|
|
283
286
|
|
|
@@ -298,7 +301,7 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
298
301
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
299
302
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
300
303
|
SOFTWARE.
|
|
301
|
-
`,
|
|
304
|
+
`,A=`# Contributor Covenant Code of Conduct
|
|
302
305
|
|
|
303
306
|
## Our Pledge
|
|
304
307
|
|
|
@@ -372,12 +375,12 @@ version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
|
|
372
375
|
|
|
373
376
|
[homepage]: http://contributor-covenant.org
|
|
374
377
|
[version]: http://contributor-covenant.org/version/1/4
|
|
375
|
-
`,
|
|
378
|
+
`,C=`node_modules/
|
|
376
379
|
.env
|
|
377
380
|
dist/
|
|
378
381
|
*.log
|
|
379
382
|
.DS_Store
|
|
380
|
-
`,
|
|
383
|
+
`,F=e=>`# \u{1F680} ${e}
|
|
381
384
|
|
|
382
385
|
This project was generated using [Shakil-Stack](https://github.com/shakil-ahmed-billal/shakil-stack-cli).
|
|
383
386
|
|
|
@@ -407,23 +410,25 @@ ${e}/
|
|
|
407
410
|
|
|
408
411
|
---
|
|
409
412
|
Built with \u26A1 by **Shakil Ahmed Billal**
|
|
410
|
-
`;var
|
|
411
|
-
\u{1F680} Initializing ${n.bold(
|
|
412
|
-
`));let d=
|
|
413
|
-
\u{1F5BC}\uFE0F Scaffolding Next.js frontend...`)),
|
|
414
|
-
\u{1F3A8} Setting up shadcn/ui...`));try{
|
|
415
|
-
\u26A0\uFE0F Warning: Failed to automate shadcn/ui init. You can run "npx shadcn@latest init" in the frontend folder.`))}}await s.outputFile(
|
|
413
|
+
`;var O=async e=>{let t=e;t||(t=(await K.prompt([{type:"input",name:"projectName",message:"What is your project name?",default:"shakil-stack-app"}])).projectName),t||(console.log(n.red("\u274C Error: Project name is required.")),process.exit(1));let{packageManager:p,useShadcn:f,installDeps:l}=await K.prompt([{type:"list",name:"packageManager",message:"Which package manager do you want to use?",choices:["pnpm","npm","yarn"],default:w()},{type:"confirm",name:"useShadcn",message:"Would you like to use shadcn/ui?",default:!0},{type:"confirm",name:"installDeps",message:"Do you want to install dependencies automatically?",default:!0}]),o=r.join(process.cwd(),t);s.existsSync(o)&&(console.log(n.red(`\u274C Error: Directory ${t} already exists.`)),process.exit(1)),console.log(n.cyan(`
|
|
414
|
+
\u{1F680} Initializing ${n.bold(t)}...
|
|
415
|
+
`));let d=ue("\u{1F6E0}\uFE0F Creating project structure...").start();try{await s.ensureDir(o),await s.ensureDir(r.join(o,"backend","src","app","config")),await s.ensureDir(r.join(o,"backend","src","app","lib")),await s.ensureDir(r.join(o,"backend","src","app","module")),await s.ensureDir(r.join(o,"backend","src","app","routes")),await s.ensureDir(r.join(o,"backend","src","app","middleware")),await s.ensureDir(r.join(o,"backend","src","app","utils")),await s.ensureDir(r.join(o,"backend","src","app","errorHelpers")),await s.ensureDir(r.join(o,"backend","prisma","schema")),console.log(n.cyan(`
|
|
416
|
+
\u{1F5BC}\uFE0F Scaffolding Next.js frontend...`)),m(`npx create-next-app@latest frontend --ts --tailwind --eslint --app --src-dir --import-alias "@/*" --use-${p}`,o);let g=["config","hooks","lib","services","types"];for(let T of g)await s.ensureDir(r.join(o,"frontend","src",T));if(f){console.log(n.cyan(`
|
|
417
|
+
\u{1F3A8} Setting up shadcn/ui...`));try{m("npx shadcn@latest init -d",r.join(o,"frontend")),console.log(n.cyan("\u{1F4E6} Adding common shadcn/ui components...")),m(`npx shadcn@latest add ${["button","card","input","label","textarea","dialog","dropdown-menu","table","tabs","checkbox"].join(" ")} -y`,r.join(o,"frontend")),console.log(n.green("\u2705 shadcn/ui and common components initialized successfully!\u2728"))}catch{console.log(n.yellow(`
|
|
418
|
+
\u26A0\uFE0F Warning: Failed to automate shadcn/ui init. You can run "npx shadcn@latest init" in the frontend folder.`))}}await s.outputFile(r.join(o,"backend","src","server.ts"),$(t)),await s.outputFile(r.join(o,"backend","src","app.ts"),_(t)),await s.outputFile(r.join(o,"backend","src","app","config","index.ts"),j),await s.outputFile(r.join(o,"backend","src","app","lib","prisma.ts"),v),await s.outputFile(r.join(o,"backend","src","app","lib","auth.ts"),k),await s.outputFile(r.join(o,"backend","src","app","routes","index.ts"),q),await s.outputFile(r.join(o,"backend","src","app","middleware","globalErrorHandler.ts"),L),await s.outputFile(r.join(o,"backend","src","app","middleware","notFound.ts"),U),await s.outputFile(r.join(o,"backend","src","app","middleware","sanitizeRequest.ts"),B),await s.outputFile(r.join(o,"backend","src","app","utils","catchAsync.ts"),H),await s.outputFile(r.join(o,"backend","src","app","utils","sendResponse.ts"),W),await s.outputFile(r.join(o,"backend","src","app","utils","sanitizer.ts"),z),await s.outputFile(r.join(o,"backend","src","app","errorHelpers","ApiError.ts"),M),await s.outputFile(r.join(o,"backend","prisma","schema","schema.prisma"),E),await s.outputFile(r.join(o,"backend","prisma","schema","auth.prisma"),G),await s.outputFile(r.join(o,"backend","prisma.config.ts"),R),await s.outputFile(r.join(o,"backend","tsconfig.json"),V),await s.outputFile(r.join(o,".gitignore"),C),await s.outputFile(r.join(o,"LICENSE"),S),await s.outputFile(r.join(o,"README.md"),F(t)),await s.outputFile(r.join(o,"CODE_OF_CONDUCT.md"),A),await s.outputFile(r.join(o,".env"),`DATABASE_URL="postgresql://postgres:postgres@localhost:5432/${t}"
|
|
416
419
|
JWT_SECRET="your-secret-key"
|
|
417
420
|
NODE_ENV="development"
|
|
418
|
-
PORT=8000
|
|
419
|
-
|
|
420
|
-
`)
|
|
421
|
-
|
|
422
|
-
`))}
|
|
421
|
+
PORT=8000
|
|
422
|
+
BETTER_AUTH_BASE_URL="http://localhost:8000"
|
|
423
|
+
CLIENT_URL="http://localhost:3000"`);let h={name:`${t}-backend`,version:"1.0.0",type:"module",scripts:{test:'echo "Error: no test specified" && exit 1',dev:"nodemon --exec tsx src/server.ts",build:"prisma generate && tsup src/server.ts --format esm --platform node --target node20 --outDir dist --external pg-native",postinstall:"prisma generate",start:"node dist/server.js","prisma:generate":"prisma generate","prisma:migrate":"prisma migrate dev","prisma:studio":"prisma studio",seed:"tsx prisma/seed.ts",setup:"pnpm install && pnpm add @prisma/adapter-pg pg && pnpm add -D @types/pg && pnpm prisma:generate",predev:"pnpm run prisma:generate",init:"pnpm run prisma:generate && pnpm run prisma:migrate --name init",lint:"eslint src/**/*.ts","lint:fix":"eslint src/**/*.ts --fix",format:"prettier --write .",push:"prisma db push",pull:"prisma db pull"},dependencies:{"@prisma/adapter-pg":"^7.5.0","@prisma/client":"^7.5.0","better-auth":"^1.5.6","cookie-parser":"^1.4.7",cors:"^2.8.6",dompurify:"^3.3.3",dotenv:"^17.3.1",express:"^5.2.1","express-rate-limit":"^8.3.1",helmet:"^8.1.0","http-status":"^2.1.0",jsdom:"^29.0.1",jsonwebtoken:"^9.0.3",morgan:"^1.10.1",pg:"^8.20.0",winston:"^3.19.0",zod:"^4.3.6"},devDependencies:{"@types/cookie-parser":"^1.4.10","@types/cors":"^2.8.19","@types/express":"^5.0.6","@types/node":"^20.19.37","@types/pg":"^8.20.0","@types/morgan":"^1.9.10","@types/jsdom":"^21.1.7",prisma:"^7.5.0",tsx:"^4.21.0",nodemon:"^3.1.14",tsup:"^8.5.1",typescript:"^5.9.3",eslint:"^9.21.0",prettier:"^3.5.2"}};await s.writeJson(r.join(o,"backend","package.json"),h,{spaces:2}),d.succeed(n.green("\u2705 Project structure created! \u2728")),l&&(console.log(n.yellow(`
|
|
424
|
+
\u{1F4E6} Finalizing dependencies with ${p}...
|
|
425
|
+
`)),m(`${p} install`,r.join(o,"backend"))),console.log(n.cyan("To get started:")),console.log(n.white(` cd ${t}`)),console.log(n.white(` cd backend && ${p} dev
|
|
426
|
+
`)),console.log(n.white(` cd frontend && ${p} dev
|
|
427
|
+
`))}catch(g){d.fail(n.red("\u274C Failed to initialize project.")),console.error(g),process.exit(1)}};import x from"fs-extra";import P from"path";import b from"chalk";import ge from"ora";var X=(e,t)=>`import { Request, Response } from 'express';
|
|
423
428
|
import httpStatus from 'http-status';
|
|
424
429
|
import catchAsync from '../../utils/catchAsync.js';
|
|
425
430
|
import sendResponse from '../../utils/sendResponse.js';
|
|
426
|
-
import { ${e}Service } from './${
|
|
431
|
+
import { ${e}Service } from './${t}.service.js';
|
|
427
432
|
|
|
428
433
|
const create${e} = catchAsync(async (req: Request, res: Response) => {
|
|
429
434
|
const result = await ${e}Service.create${e}IntoDB(req.body);
|
|
@@ -438,7 +443,7 @@ const create${e} = catchAsync(async (req: Request, res: Response) => {
|
|
|
438
443
|
export const ${e}Controller = {
|
|
439
444
|
create${e},
|
|
440
445
|
};
|
|
441
|
-
`,
|
|
446
|
+
`,Q=(e,t)=>`import { ${e} } from '@prisma/client';
|
|
442
447
|
import prisma from '../../lib/prisma.js';
|
|
443
448
|
|
|
444
449
|
const create${e}IntoDB = async (payload: any) => {
|
|
@@ -449,18 +454,18 @@ const create${e}IntoDB = async (payload: any) => {
|
|
|
449
454
|
export const ${e}Service = {
|
|
450
455
|
create${e}IntoDB,
|
|
451
456
|
};
|
|
452
|
-
`,
|
|
453
|
-
import { ${e}Controller } from './${
|
|
457
|
+
`,Z=(e,t)=>`import { Router } from 'express';
|
|
458
|
+
import { ${e}Controller } from './${t}.controller.js';
|
|
454
459
|
|
|
455
460
|
const router = Router();
|
|
456
461
|
|
|
457
|
-
router.post('/create-${
|
|
462
|
+
router.post('/create-${t}', ${e}Controller.create${e});
|
|
458
463
|
|
|
459
464
|
export const ${e}Routes = router;
|
|
460
|
-
`,
|
|
465
|
+
`,ee=e=>`export type I${e} = {
|
|
461
466
|
// Define interface
|
|
462
467
|
};
|
|
463
|
-
`,
|
|
468
|
+
`,te=e=>`import { z } from 'zod';
|
|
464
469
|
|
|
465
470
|
const create${e}ValidationSchema = z.object({
|
|
466
471
|
body: z.object({
|
|
@@ -471,11 +476,17 @@ const create${e}ValidationSchema = z.object({
|
|
|
471
476
|
export const ${e}Validations = {
|
|
472
477
|
create${e}ValidationSchema,
|
|
473
478
|
};
|
|
474
|
-
`,
|
|
475
|
-
`,
|
|
479
|
+
`,oe=e=>`export const ${e}SearchableFields = [];
|
|
480
|
+
`,re=e=>`model ${e} {
|
|
476
481
|
id String @id @default(uuid())
|
|
477
482
|
name String
|
|
478
483
|
createdAt DateTime @default(now())
|
|
479
484
|
updatedAt DateTime @updatedAt
|
|
480
485
|
}
|
|
481
|
-
`;var
|
|
486
|
+
`;var se=async e=>{e||(console.log(b.red("\u274C Error: Module name is required.")),process.exit(1));let t=e.charAt(0).toUpperCase()+e.slice(1),p=e.toLowerCase(),f=x.existsSync("backend")?"backend":".",l=P.join(f,"src","app","module",t);x.existsSync(P.join(f,"src","app","module"))||(console.log(b.red("\u274C Error: This command must be run inside your shakil-stack project root or backend directory.")),process.exit(1)),x.existsSync(l)&&(console.log(b.red(`\u274C Error: Module ${t} already exists.`)),process.exit(1));let o=ge(`\u{1F6E0}\uFE0F Generating module: ${b.cyan(t)}...`).start();try{await x.ensureDir(l);let d={"controller.ts":X(t,p),"service.ts":Q(t,p),"route.ts":Z(t,p),"interface.ts":ee(t),"validation.ts":te(t),"constant.ts":oe(t)};await x.outputFile(P.join(f,"prisma","schema",`${p}.prisma`),re(t));for(let[g,h]of Object.entries(d))await x.outputFile(P.join(l,`${p}.${g}`),h);o.succeed(b.green(`\u2705 Module ${t} generated successfully! \u2728`)),console.log(b.gray(`Created at: ${l}`))}catch(d){o.fail(b.red("\u274C Failed to generate module.")),console.error(d)}};import he from"fs-extra";import be from"chalk";var ne=async()=>{let e=w(),t=he.existsSync("backend")?"backend":".";console.log(be.cyan(`\u{1F3D7}\uFE0F Building backend with ${e}...`)),m(`${e} run build`,t)};import ye from"fs-extra";import I from"chalk";var ae=async e=>{let t=ye.existsSync("backend")?"backend":".";e==="generate"?(console.log(I.cyan("\u{1F504} Generating Prisma client...")),m("npx prisma generate --schema prisma/schema",t)):e==="migrate"?(console.log(I.cyan("\u{1F680} Running Prisma migrations...")),m("npx prisma migrate dev --schema prisma/schema",t)):console.log(I.red(`\u274C Error: Unknown prisma subcommand: ${e}`))};import i from"fs-extra";import c from"path";import u from"chalk";import xe from"ora";var ie=async()=>{let e=process.cwd(),t=c.join(e,"backend"),p=c.basename(e);i.existsSync(t)||(console.log(u.red("\u274C Error: Not in a shakil-stack project root (backend folder not found).")),process.exit(1));let f=xe("\u{1F504} Updating project structure...").start();try{let l=c.join(t,".env"),o=c.join(e,".env"),d="";i.existsSync(l)?(d=await i.readFile(l,"utf-8"),i.existsSync(o)?(console.log(u.yellow(`
|
|
487
|
+
\u26A0\uFE0F Both backend/.env and root .env exist. Merging...`)),await i.remove(l)):(await i.move(l,o),console.log(u.yellow(`
|
|
488
|
+
\u{1F4DD} Moved .env from backend/ to root.`)))):i.existsSync(o)&&(d=await i.readFile(o,"utf-8"));let g=['BETTER_AUTH_BASE_URL="http://localhost:8000"','CLIENT_URL="http://localhost:3000"'],h=d;for(let a of g){let de=a.split("=")[0];h.includes(de)||(h+=`
|
|
489
|
+
${a}`)}h!==d&&(await i.outputFile(o,h.trim()+`
|
|
490
|
+
`),console.log(u.green("\u2705 Updated root .env with missing variables.")));let T=[{path:c.join(t,"src","app","config","index.ts"),content:j},{path:c.join(t,"src","app","lib","prisma.ts"),content:v},{path:c.join(t,"src","app","lib","auth.ts"),content:k},{path:c.join(t,"prisma","schema","schema.prisma"),content:E},{path:c.join(t,"prisma.config.ts"),content:R}];for(let a of T)await i.outputFile(a.path,a.content);let le=[{path:c.join(e,".gitignore"),content:C},{path:c.join(e,"LICENSE"),content:S},{path:c.join(e,"README.md"),content:F(p)},{path:c.join(e,"CODE_OF_CONDUCT.md"),content:A}];for(let a of le)i.existsSync(a.path)||(await i.outputFile(a.path,a.content),console.log(u.cyan(`\u{1F4C4} Added ${c.basename(a.path)} to root.`)));let D=c.join(t,".gitignore");if(i.existsSync(D)){let a=await i.readFile(D,"utf-8");a.includes(".env")&&(a=a.replace(/\.env\n?/,"").trim(),await i.outputFile(D,a+`
|
|
491
|
+
`))}f.succeed(u.green("\u2705 Project updated successfully! \u2728")),console.log(u.cyan(`
|
|
492
|
+
Next Steps:`)),console.log(u.white("1. Check your root .env file and update credentials if needed.")),console.log(u.white("2. Run 'shakil-stack prisma generate' to update the Prisma client."))}catch(l){f.fail(u.red("\u274C Failed to update project.")),console.error(l),process.exit(1)}};var je=we(import.meta.url),ce=N.dirname(je),ve=N.resolve(ce,pe.existsSync(N.resolve(ce,"../../package.json"))?"../../package.json":"../package.json"),ke=pe.readJsonSync(ve),y=new Te;y.name("shakil-stack").description("Full-stack EchoNet-style project generator CLI").version(ke.version);y.command("init").description("Initialize a new full-stack project").argument("[projectName]","Name of the project").action(e=>{O(e)});y.command("update").description("Update an existing project to the latest shakil-stack version").action(()=>{ie()});y.command("generate").alias("g").description("Generate a new module").argument("<type>","Type of generation (module)").argument("<name>","Name of the module").action((e,t)=>{e==="module"?se(t):console.log(`\u274C Error: Unknown generation type: ${e}`)});y.command("build").description("Build the backend for production").action(()=>{ne()});y.command("prisma").description("Prisma utilities").argument("<subcommand>","generate | migrate").action(e=>{ae(e)});process.argv.slice(2).length?y.parse(process.argv):O();
|