@shakil-dev/shakil-stack 2.2.2 → 2.2.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/README.md +1 -1
- package/dist/index.js +23 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
## 🌟 Key Features
|
|
12
12
|
|
|
13
|
-
### 🛡️ Backend Excellence (
|
|
13
|
+
### 🛡️ Backend Excellence (Professional Coding Style)
|
|
14
14
|
- **Modular Architecture**: Clean, scalable `src/app/module` pattern.
|
|
15
15
|
- **Prisma 7+**: Next-gen database ORM with pre-configured PostgreSQL adapters.
|
|
16
16
|
- **Better Auth Integration**: Pre-built authentication schemas (User, Session, Account).
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as
|
|
2
|
+
import{Command as ie}from"commander";import Q from"fs-extra";import w from"path";import{fileURLToPath as pe}from"url";import o from"fs-extra";import s from"path";import a from"chalk";import te from"ora";import M from"inquirer";import{execSync as Z}from"child_process";import ge from"fs-extra";var i=(e,r=process.cwd())=>{try{return Z(e,{stdio:"inherit",cwd:r}),!0}catch{return!1}},h=()=>{let e=process.env.npm_config_user_agent||"";return e.includes("pnpm")?"pnpm":e.includes("yarn")?"yarn":"npm"};var j=e=>`import { Server } from 'http';
|
|
3
3
|
import app from './app.js';
|
|
4
4
|
import config from './app/config/index.js';
|
|
5
5
|
|
|
@@ -76,7 +76,7 @@ export default {
|
|
|
76
76
|
database_url: process.env.DATABASE_URL,
|
|
77
77
|
jwt_secret: process.env.JWT_SECRET,
|
|
78
78
|
};
|
|
79
|
-
`,
|
|
79
|
+
`,v=`import "dotenv/config";
|
|
80
80
|
import { PrismaClient } from "@prisma/client";
|
|
81
81
|
import pkg from 'pg';
|
|
82
82
|
import { PrismaPg } from '@prisma/adapter-pg';
|
|
@@ -90,7 +90,7 @@ const prisma = new PrismaClient({ adapter });
|
|
|
90
90
|
|
|
91
91
|
export default prisma;
|
|
92
92
|
export { prisma };
|
|
93
|
-
`,
|
|
93
|
+
`,R=`import { betterAuth } from "better-auth";
|
|
94
94
|
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
95
95
|
import config from "../config/index.js";
|
|
96
96
|
import { prisma } from "./prisma.js";
|
|
@@ -132,7 +132,7 @@ const notFound = (req: Request, res: Response, next: NextFunction) => {
|
|
|
132
132
|
};
|
|
133
133
|
|
|
134
134
|
export default notFound;
|
|
135
|
-
|
|
135
|
+
`,$=`import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
136
136
|
|
|
137
137
|
const catchAsync = (fn: RequestHandler) => {
|
|
138
138
|
return async (req: Request, res: Response, next: NextFunction) => {
|
|
@@ -145,7 +145,7 @@ const catchAsync = (fn: RequestHandler) => {
|
|
|
145
145
|
};
|
|
146
146
|
|
|
147
147
|
export default catchAsync;
|
|
148
|
-
|
|
148
|
+
`,P=`class ApiError extends Error {
|
|
149
149
|
statusCode: number;
|
|
150
150
|
constructor(statusCode: number, message: string | undefined, stack = '') {
|
|
151
151
|
super(message);
|
|
@@ -170,7 +170,7 @@ export const sanitize = (data: any): any => {
|
|
|
170
170
|
}
|
|
171
171
|
return data;
|
|
172
172
|
};
|
|
173
|
-
`,
|
|
173
|
+
`,C=`import { Request, Response, NextFunction } from 'express';
|
|
174
174
|
import { sanitize } from '../utils/sanitizer.js';
|
|
175
175
|
|
|
176
176
|
export const sanitizeRequest = (req: Request, res: Response, next: NextFunction) => {
|
|
@@ -179,7 +179,7 @@ export const sanitizeRequest = (req: Request, res: Response, next: NextFunction)
|
|
|
179
179
|
if (req.params) sanitize(req.params);
|
|
180
180
|
next();
|
|
181
181
|
};
|
|
182
|
-
`,
|
|
182
|
+
`,E=`import { Response } from 'express';
|
|
183
183
|
|
|
184
184
|
type IResponse<T> = {
|
|
185
185
|
statusCode: number;
|
|
@@ -206,6 +206,7 @@ export default sendResponse;
|
|
|
206
206
|
`,z=`generator client {
|
|
207
207
|
provider = "prisma-client-js"
|
|
208
208
|
previewFeatures = ["prismaSchemaFolder"]
|
|
209
|
+
output = "../../generated/prisma"
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
datasource db {
|
|
@@ -276,19 +277,19 @@ export default defineConfig({
|
|
|
276
277
|
"include": ["src/**/*"],
|
|
277
278
|
"exclude": ["node_modules", "dist"]
|
|
278
279
|
}
|
|
279
|
-
`;var b=async e=>{let r=e;r||(r=(await M.prompt([{type:"input",name:"projectName",message:"What is your project name?",default:"shakil-stack-app"}])).projectName),r||(console.log(
|
|
280
|
-
\u{1F680} Initializing ${
|
|
281
|
-
`));let m=
|
|
282
|
-
\u{1F5BC}\uFE0F Scaffolding Next.js frontend...`)),i(`npx create-next-app@latest frontend --ts --tailwind --eslint --app --src-dir --import-alias "@/*" --use-${
|
|
283
|
-
\u{1F3A8} Setting up shadcn/ui...`));try{i("npx shadcn@latest init -d",s.join(t,"frontend")),console.log(
|
|
284
|
-
\u26A0\uFE0F Warning: Failed to automate shadcn/ui init. You can run "npx shadcn@latest init" in the frontend folder.`))}}await o.outputFile(s.join(t,"backend","src","server.ts"),j(r)),await o.outputFile(s.join(t,"backend","src","app.ts"),T(r)),await o.outputFile(s.join(t,"backend","src","app","config","index.ts"),S),await o.outputFile(s.join(t,"backend","src","app","lib","prisma.ts"),
|
|
280
|
+
`;var b=async e=>{let r=e;r||(r=(await M.prompt([{type:"input",name:"projectName",message:"What is your project name?",default:"shakil-stack-app"}])).projectName),r||(console.log(a.red("\u274C Error: Project name is required.")),process.exit(1));let{packageManager:n,useShadcn:g,installDeps:c}=await M.prompt([{type:"list",name:"packageManager",message:"Which package manager do you want to use?",choices:["pnpm","npm","yarn"],default:h()},{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}]),t=s.join(process.cwd(),r);o.existsSync(t)&&(console.log(a.red(`\u274C Error: Directory ${r} already exists.`)),process.exit(1)),console.log(a.cyan(`
|
|
281
|
+
\u{1F680} Initializing ${a.bold(r)}...
|
|
282
|
+
`));let m=te("\u{1F6E0}\uFE0F Creating project structure...").start();try{await o.ensureDir(t),await o.ensureDir(s.join(t,"backend","src","app","config")),await o.ensureDir(s.join(t,"backend","src","app","lib")),await o.ensureDir(s.join(t,"backend","src","app","module")),await o.ensureDir(s.join(t,"backend","src","app","routes")),await o.ensureDir(s.join(t,"backend","src","app","middleware")),await o.ensureDir(s.join(t,"backend","src","app","utils")),await o.ensureDir(s.join(t,"backend","src","app","errorHelpers")),await o.ensureDir(s.join(t,"backend","prisma","schema")),console.log(a.cyan(`
|
|
283
|
+
\u{1F5BC}\uFE0F Scaffolding Next.js frontend...`)),i(`npx create-next-app@latest frontend --ts --tailwind --eslint --app --src-dir --import-alias "@/*" --use-${n}`,t);let d=["config","hooks","lib","services","types"];for(let f of d)await o.ensureDir(s.join(t,"frontend","src",f));if(g){console.log(a.cyan(`
|
|
284
|
+
\u{1F3A8} Setting up shadcn/ui...`));try{i("npx shadcn@latest init -d",s.join(t,"frontend")),console.log(a.cyan("\u{1F4E6} Adding common shadcn/ui components...")),i(`npx shadcn@latest add ${["button","card","input","label","textarea","dialog","dropdown-menu","table","tabs","checkbox"].join(" ")} -y`,s.join(t,"frontend")),console.log(a.green("\u2705 shadcn/ui and common components initialized successfully!\u2728"))}catch{console.log(a.yellow(`
|
|
285
|
+
\u26A0\uFE0F Warning: Failed to automate shadcn/ui init. You can run "npx shadcn@latest init" in the frontend folder.`))}}await o.outputFile(s.join(t,"backend","src","server.ts"),j(r)),await o.outputFile(s.join(t,"backend","src","app.ts"),T(r)),await o.outputFile(s.join(t,"backend","src","app","config","index.ts"),S),await o.outputFile(s.join(t,"backend","src","app","lib","prisma.ts"),v),await o.outputFile(s.join(t,"backend","src","app","lib","auth.ts"),R),await o.outputFile(s.join(t,"backend","src","app","routes","index.ts"),D),await o.outputFile(s.join(t,"backend","src","app","middleware","globalErrorHandler.ts"),A),await o.outputFile(s.join(t,"backend","src","app","middleware","notFound.ts"),F),await o.outputFile(s.join(t,"backend","src","app","middleware","sanitizeRequest.ts"),C),await o.outputFile(s.join(t,"backend","src","app","utils","catchAsync.ts"),$),await o.outputFile(s.join(t,"backend","src","app","utils","sendResponse.ts"),E),await o.outputFile(s.join(t,"backend","src","app","utils","sanitizer.ts"),q),await o.outputFile(s.join(t,"backend","src","app","errorHelpers","ApiError.ts"),P),await o.outputFile(s.join(t,"backend","prisma","schema","schema.prisma"),z),await o.outputFile(s.join(t,"backend","prisma","schema","auth.prisma"),I);let y=["meal.prisma","order.prisma","provider.prisma","review.prisma"];for(let f of y)await o.outputFile(s.join(t,"backend","prisma","schema",f),"// ${schema.split('.')[0].charAt(0).toUpperCase() + schema.split('.')[0].slice(1)} model\n");await o.outputFile(s.join(t,"backend","prisma.config.ts"),O),await o.outputFile(s.join(t,"backend","tsconfig.json"),_),await o.outputFile(s.join(t,"backend",".gitignore"),`node_modules
|
|
285
286
|
dist
|
|
286
287
|
.env`),await o.outputFile(s.join(t,"backend",".env"),`DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
|
|
287
|
-
JWT_SECRET="your-secret-key"`);let
|
|
288
|
-
\u{1F4E6} Finalizing dependencies with ${
|
|
289
|
-
`)),i(`${
|
|
290
|
-
`)),console.log(
|
|
291
|
-
`))}catch(d){m.fail(
|
|
288
|
+
JWT_SECRET="your-secret-key"`);let X={name:`${r}-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 o.writeJson(s.join(t,"backend","package.json"),X,{spaces:2}),m.succeed(a.green("\u2705 Project structure created! \u2728")),c&&(console.log(a.yellow(`
|
|
289
|
+
\u{1F4E6} Finalizing dependencies with ${n}...
|
|
290
|
+
`)),i(`${n} install`,s.join(t,"backend"))),console.log(a.cyan("To get started:")),console.log(a.white(` cd ${r}`)),console.log(a.white(` cd backend && ${n} dev
|
|
291
|
+
`)),console.log(a.white(` cd frontend && ${n} dev
|
|
292
|
+
`))}catch(d){m.fail(a.red("\u274C Failed to initialize project.")),console.error(d),process.exit(1)}};import l from"fs-extra";import x from"path";import p from"chalk";import se from"ora";var H=(e,r)=>`import { Request, Response } from 'express';
|
|
292
293
|
import httpStatus from 'http-status';
|
|
293
294
|
import catchAsync from '../../utils/catchAsync.js';
|
|
294
295
|
import sendResponse from '../../utils/sendResponse.js';
|
|
@@ -307,7 +308,7 @@ const create${e} = catchAsync(async (req: Request, res: Response) => {
|
|
|
307
308
|
export const ${e}Controller = {
|
|
308
309
|
create${e},
|
|
309
310
|
};
|
|
310
|
-
`,
|
|
311
|
+
`,U=(e,r)=>`import { ${e} } from '@prisma/client';
|
|
311
312
|
import prisma from '../../lib/prisma.js';
|
|
312
313
|
|
|
313
314
|
const create${e}IntoDB = async (payload: any) => {
|
|
@@ -318,7 +319,7 @@ const create${e}IntoDB = async (payload: any) => {
|
|
|
318
319
|
export const ${e}Service = {
|
|
319
320
|
create${e}IntoDB,
|
|
320
321
|
};
|
|
321
|
-
`,
|
|
322
|
+
`,L=(e,r)=>`import { Router } from 'express';
|
|
322
323
|
import { ${e}Controller } from './${r}.controller.js';
|
|
323
324
|
|
|
324
325
|
const router = Router();
|
|
@@ -326,7 +327,7 @@ const router = Router();
|
|
|
326
327
|
router.post('/create-${r}', ${e}Controller.create${e});
|
|
327
328
|
|
|
328
329
|
export const ${e}Routes = router;
|
|
329
|
-
`,
|
|
330
|
+
`,N=e=>`export type I${e} = {
|
|
330
331
|
// Define interface
|
|
331
332
|
};
|
|
332
333
|
`,B=e=>`import { z } from 'zod';
|
|
@@ -347,4 +348,4 @@ export const ${e}Validations = {
|
|
|
347
348
|
createdAt DateTime @default(now())
|
|
348
349
|
updatedAt DateTime @updatedAt
|
|
349
350
|
}
|
|
350
|
-
`;var V=async e=>{e||(console.log(p.red("\u274C Error: Module name is required.")),process.exit(1));let r=e.charAt(0).toUpperCase()+e.slice(1),
|
|
351
|
+
`;var V=async e=>{e||(console.log(p.red("\u274C Error: Module name is required.")),process.exit(1));let r=e.charAt(0).toUpperCase()+e.slice(1),n=e.toLowerCase(),g=l.existsSync("backend")?"backend":".",c=x.join(g,"src","app","module",r);l.existsSync(x.join(g,"src","app","module"))||(console.log(p.red("\u274C Error: This command must be run inside your shakil-stack project root or backend directory.")),process.exit(1)),l.existsSync(c)&&(console.log(p.red(`\u274C Error: Module ${r} already exists.`)),process.exit(1));let t=se(`\u{1F6E0}\uFE0F Generating module: ${p.cyan(r)}...`).start();try{await l.ensureDir(c);let m={"controller.ts":H(r,n),"service.ts":U(r,n),"route.ts":L(r,n),"interface.ts":N(r),"validation.ts":B(r),"constant.ts":J(r)};await l.outputFile(x.join(g,"prisma","schema",`${n}.prisma`),W(r));for(let[d,y]of Object.entries(m))await l.outputFile(x.join(c,`${n}.${d}`),y);t.succeed(p.green(`\u2705 Module ${r} generated successfully! \u2728`)),console.log(p.gray(`Created at: ${c}`))}catch(m){t.fail(p.red("\u274C Failed to generate module.")),console.error(m)}};import oe from"fs-extra";import ae from"chalk";var G=async()=>{let e=h(),r=oe.existsSync("backend")?"backend":".";console.log(ae.cyan(`\u{1F3D7}\uFE0F Building backend with ${e}...`)),i(`${e} run build`,r)};import ne from"fs-extra";import k from"chalk";var K=async e=>{let r=ne.existsSync("backend")?"backend":".";e==="generate"?(console.log(k.cyan("\u{1F504} Generating Prisma client...")),i("npx prisma generate",r)):e==="migrate"?(console.log(k.cyan("\u{1F680} Running Prisma migrations...")),i("npx prisma migrate dev",r)):console.log(k.red(`\u274C Error: Unknown prisma subcommand: ${e}`))};var ce=pe(import.meta.url),Y=w.dirname(ce),me=w.resolve(Y,Q.existsSync(w.resolve(Y,"../../package.json"))?"../../package.json":"../package.json"),de=Q.readJsonSync(me),u=new ie;u.name("shakil-stack").description("Full-stack EchoNet-style project generator CLI").version(de.version);u.command("init").description("Initialize a new full-stack project").argument("[projectName]","Name of the project").action(e=>{b(e)});u.command("generate").alias("g").description("Generate a new module").argument("<type>","Type of generation (module)").argument("<name>","Name of the module").action((e,r)=>{e==="module"?V(r):console.log(`\u274C Error: Unknown generation type: ${e}`)});u.command("build").description("Build the backend for production").action(()=>{G()});u.command("prisma").description("Prisma utilities").argument("<subcommand>","generate | migrate").action(e=>{K(e)});process.argv.slice(2).length?u.parse(process.argv):b();
|