@express-tool/cli 1.0.2 → 1.0.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 CHANGED
@@ -16,9 +16,10 @@ A production-grade Command Line Interface for generating robust, scalable Expres
16
16
  - **Language Support**: First-class TypeScript support (recommended) or modern JavaScript (ES Modules).
17
17
  - **Architecture**: Choose between **Feature-based** (great for scalability) or Classic **MVC**.
18
18
  - **Database Integration**:
19
- - **Prisma ORM**: PostgreSQL, MySQL, MongoDB.
19
+ - **Prisma ORM**: PostgreSQL (Self-managed or Managed), MySQL, MongoDB.
20
20
  - **Mongoose**: Native MongoDB support.
21
21
  - **Authentication**: Built-in simple JWT authentication boilerplate.
22
+ - **Template Engines**: Support for Server-Side Rendering (SSR) with **EJS** or **Pug**.
22
23
  - **API Documentation**: Automatic Swagger/OpenAPI options.
23
24
  - **Package Managers**: Support for `npm`, `pnpm`, `yarn`, and `bun`.
24
25
  - **Production Ready**:
@@ -97,19 +98,28 @@ express-tool init
97
98
 
98
99
  1. **Project Name**: Name of your project directory (kebab-case).
99
100
  2. **Language**: `TypeScript` (Recommended) or `JavaScript`.
100
- 3. **Architecture**:
101
+ 3. **Package Manager**: Select `npm`, `pnpm`, `yarn`, or `bun`.
102
+ 4. **Architecture**:
101
103
  - `Feature-based`: Groups files by domain feature (e.g., `src/modules/users/`).
102
104
  - `MVC`: Classic layering (`src/controllers`, `src/routes`, `src/models`).
103
- 4. **API Type**:
105
+ 5. **API Type**:
104
106
  - `REST API + Swagger`: Includes setup for auto-generated API docs.
105
107
  - `REST API (Basic)`: Simple setup without documentation tools.
106
- 5. **Database**:
108
+ 6. **Database**:
107
109
  - `PostgreSQL (Prisma)`
110
+ - `PostgreSQL (Prisma Postgres Managed)`
108
111
  - `MySQL (Prisma)`
109
- - `MongoDB (Prisma)`
110
112
  - `MongoDB (Mongoose)`
111
113
  - `None`
112
- 6. **Package Manager**: Select `npm`, `pnpm`, `yarn`, or `bun`.
114
+ 7. **Authentication**:
115
+ - `JWT (JSON Web Token)`
116
+ - `None`
117
+ 8. **Template Engine**:
118
+ - `EJS`
119
+ - `Pug`
120
+ - `None (API only)`
121
+ 9. **Linting**: (Yes/No) Include ESLint & Prettier.
122
+ 10. **Runtime Validation**: (Yes/No) Include Zod.
113
123
 
114
124
  ---
115
125
 
@@ -176,7 +186,7 @@ A typical project created with `@express-tool/cli` looks like this:
176
186
  my-express-app/
177
187
  ├── 🐳 .dockerignore
178
188
  ├── ⚙️ .env
179
- ├── ⚙️ .eslintrc.json
189
+ ├── ⚙️ eslint.config.mjs
180
190
  ├── 🐙 .github/ # CI/CD Workflows
181
191
  ├── 🙈 .gitignore
182
192
  ├── 💅 .prettierrc
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{createRequire as e}from"module";e(import.meta.url);import{Command as t}from"commander";import s from"picocolors";import{authPlugin as n}from"@express-tool/plugin-auth";import{ciPlugin as a}from"@express-tool/plugin-ci";import{commonPlugin as o}from"@express-tool/plugin-common";import{databasePlugin as r}from"@express-tool/plugin-database";import{dockerPlugin as i}from"@express-tool/plugin-docker";import{middlewarePlugin as p}from"@express-tool/plugin-middleware";import{qualityPlugin as l}from"@express-tool/plugin-quality";import{swaggerPlugin as c}from"@express-tool/plugin-swagger";import{testingPlugin as m}from"@express-tool/plugin-testing";import{viewsPlugin as g}from"@express-tool/plugin-views";import d from"fs-extra";import u from"prompts";import{z as y}from"zod";import{controllerJs as f,controllerTs as w,routesJs as v,routesTs as j,testJs as h,testTs as x}from"@express-tool/plugin-resource";import S from"envinfo";import{createRequire as $}from"node:module";let b=$(import.meta.url);var C={656(e,$,C){C.d($,{x:()=>G});var k=JSON.parse('{"UU":"@express-tool/cli","rE":"1.0.2"}');let E=e=>console.log(s.blue("ℹ"),e),N=e=>console.log(s.green("✔"),e),D=e=>console.log(s.yellow("⚠"),e),P=e=>console.log(s.red("✖"),e),R=b("path");var T=C.n(R);async function M(e,t,s,n,a,o){E(`Applying plugin: ${e.name}`);let r=await e.apply(t,o);if(r.dependencies&&(n.dependencies={...n.dependencies,...r.dependencies}),r.devDependencies&&(n.devDependencies={...n.devDependencies,...r.devDependencies}),r.files)for(let e of r.files){let t=T().join(s,"src",e.path);d.ensureDirSync(T().dirname(t)),d.writeFileSync(t,e.content),E(` Created ${e.path}`)}if(r.env)for(let[e,t]of Object.entries(r.env))a.push(`${e}=${t}`);if(r.scripts&&(n.scripts={...n.scripts,...r.scripts}),r.commands)for(let e of r.commands){E(` Running command: ${e}`);try{C(317).execSync(e,{cwd:s,stdio:"inherit"})}catch(t){D(` Command failed: ${e}`)}}return r}async function L(e){let t=T().resolve(process.cwd(),e.projectName);d.existsSync(t)&&(P(`Directory ${e.projectName} already exists.`),process.exit(1)),E(`Creating project in ${t}...`),d.mkdirSync(t);let s=[],u={name:e.projectName,version:"0.0.0",private:!0,type:"module",scripts:{dev:"ts"===e.language?"nodemon --exec ts-node src/index.ts":"nodemon src/index.js",build:"ts"===e.language?"tsup src/index.ts --format esm --clean":void 0,start:"ts"===e.language?"node dist/index.js":"node src/index.js"},dependencies:{express:"^4.21.0"},devDependencies:{..."ts"===e.language?{"@types/node":"^20.12.0","@types/express":"^4.17.21",tsup:"^8.1.0"}:{}},engines:{node:">=20.0.0"}};if("rest-swagger"===e.apiType&&(Object.assign(u.dependencies,{"swagger-ui-express":"^5.0.0",zod:"^3.23.8","@asteasolutions/zod-to-openapi":"^7.3.0"}),"ts"===e.language&&Object.assign(u.devDependencies,{"@types/swagger-ui-express":"^4.1.6"})),"jwt"===e.auth&&(Object.assign(u.dependencies,{jsonwebtoken:"^9.0.2"}),"ts"===e.language&&Object.assign(u.devDependencies,{"@types/jsonwebtoken":"^9.0.6"})),"none"!==e.templateEngine){let t="ejs"===e.templateEngine?"^3.1.10":"^3.0.3";if(Object.assign(u.dependencies,{[e.templateEngine]:t}),"ts"===e.language){let t="ejs"===e.templateEngine?"^3.1.5":"^2.0.10";Object.assign(u.devDependencies,{[`@types/${e.templateEngine}`]:t})}}d.writeJsonSync(T().join(t,"package.json"),u,{spaces:2});let y=T().join(t,"src");d.mkdirSync(y),e.language;let f={projectRoot:t,projectName:e.projectName,isTs:"ts"===e.language,language:e.language};await M(o,f,t,u,s),e.validation&&await M(p,f,t,u,s),"rest-swagger"===e.apiType&&await M(c,f,t,u,s),"none"!==e.database&&await M(r,f,t,u,s,{database:e.database}),"jwt"===e.auth&&await M(n,f,t,u,s),"none"!==e.templateEngine&&await M(g,f,t,u,s,{templateEngine:e.templateEngine});let w=s.join("\n")+"\n";d.writeFileSync(T().join(t,".env"),w),d.writeFileSync(T().join(t,".env.example"),w);let v=`import 'dotenv/config';
2
+ import{createRequire as e}from"module";e(import.meta.url);import{Command as t}from"commander";import s from"picocolors";import{authPlugin as n}from"@express-tool/plugin-auth";import{ciPlugin as a}from"@express-tool/plugin-ci";import{commonPlugin as o}from"@express-tool/plugin-common";import{databasePlugin as r}from"@express-tool/plugin-database";import{dockerPlugin as i}from"@express-tool/plugin-docker";import{middlewarePlugin as p}from"@express-tool/plugin-middleware";import{qualityPlugin as l}from"@express-tool/plugin-quality";import{swaggerPlugin as c}from"@express-tool/plugin-swagger";import{testingPlugin as g}from"@express-tool/plugin-testing";import{viewsPlugin as m}from"@express-tool/plugin-views";import u from"fs-extra";import d from"prompts";import{z as y}from"zod";import{controllerJs as f,controllerTs as w,routesJs as j,routesTs as v,testJs as h,testTs as x}from"@express-tool/plugin-resource";import S from"envinfo";import{createRequire as $}from"node:module";let b=$(import.meta.url);var k={656(e,$,k){k.d($,{x:()=>G});var C=JSON.parse('{"UU":"@express-tool/cli","rE":"1.0.3"}');let E=e=>console.log(s.blue("ℹ"),e),D=e=>console.log(s.green("✔"),e),P=e=>console.log(s.yellow("⚠"),e),R=e=>console.log(s.red("✖"),e),T=b("path");var N=k.n(T);async function M(e,t,s,n,a,o){E(`Applying plugin: ${e.name}`);let r=await e.apply(t,o);if(r.dependencies&&(n.dependencies={...n.dependencies,...r.dependencies}),r.devDependencies&&(n.devDependencies={...n.devDependencies,...r.devDependencies}),r.files)for(let e of r.files){let t=N().join(s,"src",e.path);u.ensureDirSync(N().dirname(t)),u.writeFileSync(t,e.content),E(` Created ${e.path}`)}if(r.env)for(let[e,t]of Object.entries(r.env))a.push(`${e}=${t}`);if(r.scripts&&(n.scripts={...n.scripts,...r.scripts}),r.commands)for(let e of r.commands){E(` Running command: ${e}`);try{k(317).execSync(e,{cwd:s,stdio:"inherit"})}catch(t){P(` Command failed: ${e}`)}}return r}async function L(e){let t=N().resolve(process.cwd(),e.projectName);u.existsSync(t)&&(R(`Directory ${e.projectName} already exists.`),process.exit(1)),E(`Creating project in ${t}...`),u.mkdirSync(t);let s=[],d={name:e.projectName,version:"0.0.0",private:!0,type:"module",scripts:{dev:"ts"===e.language?"tsx watch src/index.ts":"nodemon --experimental-specifier-resolution=node src/index.js",build:"ts"===e.language?"tsup src/index.ts --format esm --platform node --target es2022 --clean":void 0,start:"ts"===e.language?"node dist/index.js":"node --experimental-specifier-resolution=node src/index.js",..."ts"===e.language?{typecheck:"tsc --noEmit",test:"vitest"}:{}},dependencies:{express:"^5.2.1"},devDependencies:{..."ts"===e.language?{"@types/node":"^25.2.2","@types/express":"^5.0.0","@types/express-serve-static-core":"^5.0.6",tsup:"^8.1.0",tsx:"^4.19.2",typescript:"^5.7.0"}:{}},engines:{node:">=20.0.0"}};if("rest-swagger"===e.apiType&&(Object.assign(d.dependencies,{"swagger-ui-express":"^5.0.0",zod:"^4.3.6","@asteasolutions/zod-to-openapi":"^7.3.0"}),"ts"===e.language&&Object.assign(d.devDependencies,{"@types/swagger-ui-express":"^4.1.6"})),"jwt"===e.auth&&(Object.assign(d.dependencies,{jsonwebtoken:"^9.0.2"}),"ts"===e.language&&Object.assign(d.devDependencies,{"@types/jsonwebtoken":"^9.0.6"})),"none"!==e.templateEngine){let t="ejs"===e.templateEngine?"^3.1.10":"^3.0.3";if(Object.assign(d.dependencies,{[e.templateEngine]:t}),"ts"===e.language){let t="ejs"===e.templateEngine?"^3.1.5":"^2.0.10";Object.assign(d.devDependencies,{[`@types/${e.templateEngine}`]:t})}}u.writeJsonSync(N().join(t,"package.json"),d,{spaces:2});let y=N().join(t,"src");u.mkdirSync(y),e.language;let f={projectRoot:t,projectName:e.projectName,isTs:"ts"===e.language,language:e.language};await M(o,f,t,d,s),e.validation&&await M(p,f,t,d,s),"rest-swagger"===e.apiType&&await M(c,f,t,d,s),"none"!==e.database&&await M(r,f,t,d,s,{database:e.database}),"jwt"===e.auth&&await M(n,f,t,d,s),"none"!==e.templateEngine&&await M(m,f,t,d,s,{templateEngine:e.templateEngine});let w=s.join("\n")+"\n";u.writeFileSync(N().join(t,".env"),w),u.writeFileSync(N().join(t,".env.example"),w);let j=`import 'dotenv/config';
3
3
  import express${"ts"===e.language?", { Request, Response }":""} from 'express';
4
4
  import cors from 'cors';
5
5
  import helmet from 'helmet';
@@ -7,15 +7,16 @@ import path from 'path';
7
7
  import { fileURLToPath } from 'url';
8
8
  import pinoHttp from 'pino-http';
9
9
  import { rateLimit } from 'express-rate-limit';
10
- import { logger } from './utils/logger';
11
- import { errorHandler } from './middleware/errorHandler';
12
- ${"mongodb"===e.database?"import { connectDB } from './lib/db';":""}
10
+ import { logger } from './utils/logger${"ts"===e.language?"":".js"}';
11
+ import { errorHandler } from './middleware/errorHandler${"ts"===e.language?"":".js"}';
12
+ ${"mongodb"===e.database?`import { connectDB } from './lib/db${"ts"===e.language?"":".js"}';`:""}
13
13
 
14
14
  const __filename = fileURLToPath(import.meta.url);
15
15
  const __dirname = path.dirname(__filename);
16
16
 
17
- ${"jwt"===e.auth?"import { authRouter } from './routes/auth';\nimport { authenticateToken } from './middleware/auth';":""}
18
- ${"rest-swagger"===e.apiType?"import { swaggerRouter } from './docs/index';":""}
17
+ ${"jwt"===e.auth?`import { authRouter } from './routes/auth${"ts"===e.language?"":".js"}';
18
+ import { authenticateToken } from './middleware/auth${"ts"===e.language?"":".js"}';`:""}
19
+ ${"rest-swagger"===e.apiType?`import { swaggerRouter } from './docs/index${"ts"===e.language?"":".js"}';`:""}
19
20
 
20
21
  const app = express();
21
22
  const port = process.env.PORT || 3000;
@@ -42,20 +43,20 @@ app.set('view engine', '${e.templateEngine}');
42
43
  app.use(express.static(path.join(__dirname, 'public')));
43
44
  `:""}
44
45
 
45
- `;"rest-swagger"===e.apiType&&(v+=`app.use('/docs', swaggerRouter);
46
- `),"jwt"===e.auth&&(v+=`app.use('/auth', authRouter);
47
- `),v+=`app.get('/', (req${"ts"===e.language?": Request":""}, res${"ts"===e.language?": Response":""}) => {
46
+ `;"rest-swagger"===e.apiType&&(j+=`app.use('/docs', swaggerRouter);
47
+ `),"jwt"===e.auth&&(j+=`app.use('/auth', authRouter);
48
+ `),j+=`app.get('/', (req${"ts"===e.language?": Request":""}, res${"ts"===e.language?": Response":""}) => {
48
49
  ${"none"!==e.templateEngine?`res.render('index', { title: 'Express App', message: 'Hello from ${e.templateEngine.toUpperCase()}!' });`:"res.json({ status: 'ok', timestamp: new Date().toISOString() });"}
49
50
  });
50
51
 
51
52
  app.get('/health', (req${"ts"===e.language?": Request":""}, res${"ts"===e.language?": Response":""}) => {
52
53
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
53
54
  });
54
- `,"jwt"===e.auth&&(v+=`
55
+ `,"jwt"===e.auth&&(j+=`
55
56
  app.get('/protected', authenticateToken, (req${"ts"===e.language?": Request":""}, res${"ts"===e.language?": Response":""}) => {
56
57
  res.json({ message: 'This is a protected route', user: (req as any).user });
57
58
  });
58
- `),v+=`
59
+ `),j+=`
59
60
  app.use(errorHandler);
60
61
 
61
62
  // Export app for testing
@@ -79,12 +80,12 @@ if (process.env.NODE_ENV !== 'test') {
79
80
  process.on('SIGTERM', shutdown);
80
81
  process.on('SIGINT', shutdown);
81
82
  }
82
- `;let j="ts"===e.language?"index.ts":"index.js";d.writeFileSync(T().join(y,j),v),await M(m,f,t,u,s),e.linting&&await M(l,f,t,u,s),await M(i,f,t,u,s,{packageManager:e.packageManager,database:e.database}),await M(a,f,t,u,s,{packageManager:e.packageManager}),"ts"===e.language&&d.writeJsonSync(T().join(t,"tsconfig.json"),{compilerOptions:{target:"ES2022",module:"NodeNext",moduleResolution:"NodeNext",outDir:"./dist",rootDir:"./src",strict:!0,esModuleInterop:!0,skipLibCheck:!0,forceConsistentCasingInFileNames:!0},include:["src/**/*"]},{spaces:2});let h=`node_modules
83
+ `;let v="ts"===e.language?"index.ts":"index.js";u.writeFileSync(N().join(y,v),j),await M(g,f,t,d,s),e.linting&&await M(l,f,t,d,s),await M(i,f,t,d,s,{packageManager:e.packageManager,database:e.database}),await M(a,f,t,d,s,{packageManager:e.packageManager}),"ts"===e.language&&u.writeJsonSync(N().join(t,"tsconfig.json"),{compilerOptions:{target:"ES2022",lib:["ES2022"],module:"ESNext",moduleResolution:"bundler",rootDir:"./src",outDir:"./dist",strict:!0,esModuleInterop:!0,skipLibCheck:!0,declaration:!0,sourceMap:!0,noEmit:!0},include:["src/**/*"]},{spaces:2});let h=`node_modules
83
84
  dist
84
85
  .env
85
86
  .DS_Store
86
- `;d.writeFileSync(T().join(t,".gitignore"),h),d.writeJsonSync(T().join(t,"package.json"),u,{spaces:2}),E("Installing dependencies...");try{let s=(e.packageManager,"install");C(317).execSync(`${e.packageManager} ${s}`,{cwd:t,stdio:"inherit"})}catch(e){D("Failed to install dependencies. Please run install manually.")}N(`Project ${e.projectName} created successfully!`),console.log(`
87
+ `;u.writeFileSync(N().join(t,".gitignore"),h),u.writeJsonSync(N().join(t,"package.json"),d,{spaces:2}),E("Installing dependencies...");try{let s=(e.packageManager,"install");k(317).execSync(`${e.packageManager} ${s}`,{cwd:t,stdio:"inherit"})}catch(e){P("Failed to install dependencies. Please run install manually.")}D(`Project ${e.projectName} created successfully!`),console.log(`
87
88
  Next steps:`),console.log(` cd ${e.projectName}`),console.log(` ${e.packageManager} run dev`),"prisma-postgres"===e.database&&console.log(` ${e.packageManager} run db:init (To setup Prisma Postgres Managed DB)`),console.log(`
88
89
  Happy Coding! 🚀
89
- `)}let O=y.object({projectName:y.string().min(1).regex(/^[a-z0-9-]+$/,"Project name must be kebab-case (lowercase letters, numbers, and hyphens)"),language:y.enum(["ts","js"]),architecture:y.enum(["feature","mvc"]),apiType:y.enum(["rest","rest-swagger"]),database:y.enum(["postgresql","prisma-postgres","mysql","mongodb","none"]),auth:y.enum(["jwt","none"]),templateEngine:y.enum(["ejs","pug","none"]),packageManager:y.enum(["npm","pnpm","yarn","bun"]),linting:y.boolean(),validation:y.boolean()});async function I(){return await u([{type:"text",name:"projectName",message:"What is your project name?",initial:"my-express-app",validate:e=>{let t=O.shape.projectName.safeParse(e);return!!t.success||t.error.issues[0].message}},{type:"select",name:"language",message:"Select language",choices:[{title:"TypeScript (Recommended)",value:"ts"},{title:"JavaScript",value:"js"}],initial:0},{type:"select",name:"packageManager",message:"Select package manager",choices:[{title:"pnpm (Recommended)",value:"pnpm"},{title:"npm",value:"npm"},{title:"Yarn",value:"yarn"},{title:"Bun",value:"bun"}],initial:0},{type:"select",name:"architecture",message:"Select architecture",choices:[{title:"Feature-based (Recommended)",value:"feature",description:"Modules grouped by feature (e.g., users, posts)"},{title:"MVC",value:"mvc",description:"Classic Model-View-Controller structure"}],initial:0},{type:"select",name:"apiType",message:"Select API type",choices:[{title:"REST API + Swagger",value:"rest-swagger"},{title:"REST API (Basic)",value:"rest"}],initial:0},{type:"select",name:"database",message:"Select database",choices:[{title:"PostgreSQL (Prisma)",value:"postgresql"},{title:"PostgreSQL (Prisma Postgres Managed)",value:"prisma-postgres"},{title:"MySQL (Prisma)",value:"mysql"},{title:"MongoDB (Mongoose)",value:"mongodb"},{title:"None",value:"none"}],initial:0},{type:"select",name:"auth",message:"Select authentication",choices:[{title:"JWT (JSON Web Token)",value:"jwt"},{title:"None",value:"none"}],initial:0},{type:"select",name:"templateEngine",message:"Select template engine",choices:[{title:"EJS (Embedded JavaScript)",value:"ejs"},{title:"Pug (Jade)",value:"pug"},{title:"None (API only)",value:"none"}],initial:0},{type:"confirm",name:"linting",message:"Do you want to include code quality tools (Linting & Formatting)?",initial:!0},{type:"confirm",name:"validation",message:"Do you want to include runtime validation (Zod)?",initial:!0}],{onCancel:()=>{P("Operation cancelled"),process.exit(1)}})}let F=new t("init").description("Initialize a new Express.js project").action(async()=>{let e=await I();await L(e)});async function _(e){let t=process.cwd(),s=T().join(t,"package.json");d.existsSync(s)||(P("No package.json found. Are you in the root of the project?"),process.exit(1));let n=d.readJsonSync(s),a=d.existsSync(T().join(t,"tsconfig.json"))||n.devDependencies&&n.devDependencies.typescript||n.dependencies&&n.dependencies.typescript,o=a?"ts":"js",r=T().join(t,"src"),i=T().join(r,"controllers"),p=T().join(r,"routes"),l=T().join(t,"test");d.ensureDirSync(i),d.ensureDirSync(p),d.ensureDirSync(l);let c=e.charAt(0).toUpperCase()+e.slice(1),m=T().join(i,`${e.toLowerCase()}.controller.${o}`);d.existsSync(m)?D(`Controller ${m} already exists. Skipping.`):(d.writeFileSync(m,a?w(c):f(c)),E(`Created controller: src/controllers/${e.toLowerCase()}.controller.${o}`));let g=T().join(p,`${e.toLowerCase()}.routes.${o}`);d.existsSync(g)?D(`Route ${g} already exists. Skipping.`):(d.writeFileSync(g,a?j(c):v(c)),E(`Created route: src/routes/${e.toLowerCase()}.routes.${o}`));let u=T().join(l,`${e.toLowerCase()}.test.${o}`);d.existsSync(u)?D(`Test ${u} already exists. Skipping.`):(d.writeFileSync(u,a?x(c):h(c)),E(`Created test: test/${e.toLowerCase()}.test.${o}`)),N(`Feature ${e} generated successfully!`),E(`
90
- Don't forget to register the route in src/index.${o}:`),E(`import { ${e.toLowerCase()}Router } from './routes/${e.toLowerCase()}.routes';`),E(`app.use('/${e.toLowerCase()}s', ${e.toLowerCase()}Router);`)}let q=new t().name("generate").alias("g").description("Generate a new feature (controller, routes, test)").argument("<name>","Name of the feature (e.g. users, blogs)").action(async e=>{await _(e)}),J=new t().name("info").description("Print debugging information about your environment").action(async()=>{console.log(" System:"),await S.run({System:["OS","CPU","Memory","Shell"],Binaries:["Node","Yarn","npm","pnpm"],Utilities:["Git"],IDEs:["VSCode"],Browsers:["Chrome","Edge","Firefox","Safari"],npmPackages:["typescript","express","express-tool"]},{console:!0,showNotFound:!0})}),H=b("https");var U=C.n(H);let A=new t().name("upgrade").description("Check for updates").action(async()=>{E("Checking for updates...");let e=k.rE,t=k.UU;try{var s;let n=await (s=t,new Promise(e=>{U().get(`https://registry.npmjs.org/${s}/latest`,t=>{let s="";t.on("data",e=>s+=e),t.on("end",()=>{try{let t=JSON.parse(s);e(t.version)}catch{e(null)}})}).on("error",()=>e(null))}));if(!n)return void D("Could not fetch latest version info.");n!==e?(E(`New version available: ${n} (current: ${e})`),E(`Run 'npm install -g ${t}' to update.`)):N("You are using the latest version.")}catch(e){P("Failed to check for updates.")}}),{rE:B}=k,G=new t;G.name("express-tool").description("Production-grade CLI for Express.js applications").version(B),G.hook("preAction",()=>{E("Welcome to express-tool CLI")}),G.addCommand(F),G.addCommand(q),G.addCommand(J),G.addCommand(A)},317(e){e.exports=b("child_process")}},k={};function E(e){var t=k[e];if(void 0!==t)return t.exports;var s=k[e]={exports:{}};return C[e](s,s.exports,E),s.exports}E.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return E.d(t,{a:t}),t},E.d=(e,t)=>{for(var s in t)E.o(t,s)&&!E.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},E.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),E(656).x.parse(process.argv);
90
+ `)}let O=y.object({projectName:y.string().min(1).regex(/^[a-z0-9-]+$/,"Project name must be kebab-case (lowercase letters, numbers, and hyphens)"),language:y.enum(["ts","js"]),architecture:y.enum(["feature","mvc"]),apiType:y.enum(["rest","rest-swagger"]),database:y.enum(["postgresql","prisma-postgres","mysql","mongodb","none"]),auth:y.enum(["jwt","none"]),templateEngine:y.enum(["ejs","pug","none"]),packageManager:y.enum(["npm","pnpm","yarn","bun"]),linting:y.boolean(),validation:y.boolean()});async function I(){return await d([{type:"text",name:"projectName",message:"What is your project name?",initial:"my-express-app",validate:e=>{let t=O.shape.projectName.safeParse(e);return!!t.success||t.error.issues[0].message}},{type:"select",name:"language",message:"Select language",choices:[{title:"TypeScript (Recommended)",value:"ts"},{title:"JavaScript",value:"js"}],initial:0},{type:"select",name:"packageManager",message:"Select package manager",choices:[{title:"pnpm (Recommended)",value:"pnpm"},{title:"npm",value:"npm"},{title:"Yarn",value:"yarn"},{title:"Bun",value:"bun"}],initial:0},{type:"select",name:"architecture",message:"Select architecture",choices:[{title:"Feature-based (Recommended)",value:"feature",description:"Modules grouped by feature (e.g., users, posts)"},{title:"MVC",value:"mvc",description:"Classic Model-View-Controller structure"}],initial:0},{type:"select",name:"apiType",message:"Select API type",choices:[{title:"REST API + Swagger",value:"rest-swagger"},{title:"REST API (Basic)",value:"rest"}],initial:0},{type:"select",name:"database",message:"Select database",choices:[{title:"PostgreSQL (Prisma)",value:"postgresql"},{title:"PostgreSQL (Prisma Postgres Managed)",value:"prisma-postgres"},{title:"MySQL (Prisma)",value:"mysql"},{title:"MongoDB (Mongoose)",value:"mongodb"},{title:"None",value:"none"}],initial:0},{type:"select",name:"auth",message:"Select authentication",choices:[{title:"JWT (JSON Web Token)",value:"jwt"},{title:"None",value:"none"}],initial:0},{type:"select",name:"templateEngine",message:"Select template engine",choices:[{title:"EJS (Embedded JavaScript)",value:"ejs"},{title:"Pug (Jade)",value:"pug"},{title:"None (API only)",value:"none"}],initial:0},{type:"confirm",name:"linting",message:"Do you want to include code quality tools (Linting & Formatting)?",initial:!0},{type:"confirm",name:"validation",message:"Do you want to include runtime validation (Zod)?",initial:!0}],{onCancel:()=>{R("Operation cancelled"),process.exit(1)}})}let _=new t("init").description("Initialize a new Express.js project").action(async()=>{let e=await I();await L(e)});async function q(e){let t=process.cwd(),s=N().join(t,"package.json");u.existsSync(s)||(R("No package.json found. Are you in the root of the project?"),process.exit(1));let n=u.readJsonSync(s),a=u.existsSync(N().join(t,"tsconfig.json"))||n.devDependencies&&n.devDependencies.typescript||n.dependencies&&n.dependencies.typescript,o=a?"ts":"js",r=N().join(t,"src"),i=N().join(r,"controllers"),p=N().join(r,"routes"),l=N().join(t,"test");u.ensureDirSync(i),u.ensureDirSync(p),u.ensureDirSync(l);let c=e.charAt(0).toUpperCase()+e.slice(1),g=N().join(i,`${e.toLowerCase()}.controller.${o}`);u.existsSync(g)?P(`Controller ${g} already exists. Skipping.`):(u.writeFileSync(g,a?w(c):f(c)),E(`Created controller: src/controllers/${e.toLowerCase()}.controller.${o}`));let m=N().join(p,`${e.toLowerCase()}.routes.${o}`);u.existsSync(m)?P(`Route ${m} already exists. Skipping.`):(u.writeFileSync(m,a?v(c):j(c)),E(`Created route: src/routes/${e.toLowerCase()}.routes.${o}`));let d=N().join(l,`${e.toLowerCase()}.test.${o}`);u.existsSync(d)?P(`Test ${d} already exists. Skipping.`):(u.writeFileSync(d,a?x(c):h(c)),E(`Created test: test/${e.toLowerCase()}.test.${o}`)),D(`Feature ${e} generated successfully!`),E(`
91
+ Don't forget to register the route in src/index.${o}:`),E(`import { ${e.toLowerCase()}Router } from './routes/${e.toLowerCase()}.routes${a?"":".js"}';`),E(`app.use('/${e.toLowerCase()}s', ${e.toLowerCase()}Router);`)}let F=new t().name("generate").alias("g").description("Generate a new feature (controller, routes, test)").argument("<name>","Name of the feature (e.g. users, blogs)").action(async e=>{await q(e)}),J=new t().name("info").description("Print debugging information about your environment").action(async()=>{console.log(" System:"),await S.run({System:["OS","CPU","Memory","Shell"],Binaries:["Node","Yarn","npm","pnpm"],Utilities:["Git"],IDEs:["VSCode"],Browsers:["Chrome","Edge","Firefox","Safari"],npmPackages:["typescript","express","express-tool"]},{console:!0,showNotFound:!0})}),H=b("https");var U=k.n(H);let A=new t().name("upgrade").description("Check for updates").action(async()=>{E("Checking for updates...");let e=C.rE,t=C.UU;try{var s;let n=await (s=t,new Promise(e=>{U().get(`https://registry.npmjs.org/${s}/latest`,t=>{let s="";t.on("data",e=>s+=e),t.on("end",()=>{try{let t=JSON.parse(s);e(t.version)}catch{e(null)}})}).on("error",()=>e(null))}));if(!n)return void P("Could not fetch latest version info.");n!==e?(E(`New version available: ${n} (current: ${e})`),E(`Run 'npm install -g ${t}' to update.`)):D("You are using the latest version.")}catch(e){R("Failed to check for updates.")}}),{rE:B}=C,G=new t;G.name("express-tool").description("Production-grade CLI for Express.js applications").version(B),G.hook("preAction",()=>{E("Welcome to express-tool CLI")}),G.addCommand(_),G.addCommand(F),G.addCommand(J),G.addCommand(A)},317(e){e.exports=b("child_process")}},C={};function E(e){var t=C[e];if(void 0!==t)return t.exports;var s=C[e]={exports:{}};return k[e](s,s.exports,E),s.exports}E.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return E.d(t,{a:t}),t},E.d=(e,t)=>{for(var s in t)E.o(t,s)&&!E.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},E.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),E(656).x.parse(process.argv);
@@ -1 +1 @@
1
- {"version":3,"file":"init.generator.d.ts","sourceRoot":"","sources":["../../src/commands/init.generator.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,wBAAsB,eAAe,CAAC,OAAO,EAAE,WAAW,iBAoTzD"}
1
+ {"version":3,"file":"init.generator.d.ts","sourceRoot":"","sources":["../../src/commands/init.generator.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,wBAAsB,eAAe,CAAC,OAAO,EAAE,WAAW,iBAuUzD"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{createRequire as e}from"node:module";let t=e(import.meta.url);import{Command as s}from"commander";import n from"picocolors";import{authPlugin as a}from"@express-tool/plugin-auth";import{ciPlugin as o}from"@express-tool/plugin-ci";import{commonPlugin as r}from"@express-tool/plugin-common";import{databasePlugin as i}from"@express-tool/plugin-database";import{dockerPlugin as p}from"@express-tool/plugin-docker";import{middlewarePlugin as l}from"@express-tool/plugin-middleware";import{qualityPlugin as c}from"@express-tool/plugin-quality";import{swaggerPlugin as m}from"@express-tool/plugin-swagger";import{testingPlugin as g}from"@express-tool/plugin-testing";import{viewsPlugin as d}from"@express-tool/plugin-views";import u from"fs-extra";import y from"prompts";import{z as f}from"zod";import{controllerJs as w,controllerTs as v,routesJs as j,routesTs as h,testJs as x,testTs as S}from"@express-tool/plugin-resource";import $ from"envinfo";var b={317(e){e.exports=t("child_process")}},C={};function k(e){var t=C[e];if(void 0!==t)return t.exports;var s=C[e]={exports:{}};return b[e](s,s.exports,k),s.exports}k.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return k.d(t,{a:t}),t},k.d=(e,t)=>{for(var s in t)k.o(t,s)&&!k.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},k.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var E={};(()=>{k.d(E,{x:()=>B});var e=JSON.parse('{"UU":"@express-tool/cli","rE":"1.0.2"}');let b=e=>console.log(n.blue("ℹ"),e),C=e=>console.log(n.green("✔"),e),N=e=>console.log(n.yellow("⚠"),e),D=e=>console.log(n.red("✖"),e),P=t("path");var R=k.n(P);async function T(e,t,s,n,a,o){b(`Applying plugin: ${e.name}`);let r=await e.apply(t,o);if(r.dependencies&&(n.dependencies={...n.dependencies,...r.dependencies}),r.devDependencies&&(n.devDependencies={...n.devDependencies,...r.devDependencies}),r.files)for(let e of r.files){let t=R().join(s,"src",e.path);u.ensureDirSync(R().dirname(t)),u.writeFileSync(t,e.content),b(` Created ${e.path}`)}if(r.env)for(let[e,t]of Object.entries(r.env))a.push(`${e}=${t}`);if(r.scripts&&(n.scripts={...n.scripts,...r.scripts}),r.commands)for(let e of r.commands){b(` Running command: ${e}`);try{k(317).execSync(e,{cwd:s,stdio:"inherit"})}catch(t){N(` Command failed: ${e}`)}}return r}async function M(e){let t=R().resolve(process.cwd(),e.projectName);u.existsSync(t)&&(D(`Directory ${e.projectName} already exists.`),process.exit(1)),b(`Creating project in ${t}...`),u.mkdirSync(t);let s=[],n={name:e.projectName,version:"0.0.0",private:!0,type:"module",scripts:{dev:"ts"===e.language?"nodemon --exec ts-node src/index.ts":"nodemon src/index.js",build:"ts"===e.language?"tsup src/index.ts --format esm --clean":void 0,start:"ts"===e.language?"node dist/index.js":"node src/index.js"},dependencies:{express:"^4.21.0"},devDependencies:{..."ts"===e.language?{"@types/node":"^20.12.0","@types/express":"^4.17.21",tsup:"^8.1.0"}:{}},engines:{node:">=20.0.0"}};if("rest-swagger"===e.apiType&&(Object.assign(n.dependencies,{"swagger-ui-express":"^5.0.0",zod:"^3.23.8","@asteasolutions/zod-to-openapi":"^7.3.0"}),"ts"===e.language&&Object.assign(n.devDependencies,{"@types/swagger-ui-express":"^4.1.6"})),"jwt"===e.auth&&(Object.assign(n.dependencies,{jsonwebtoken:"^9.0.2"}),"ts"===e.language&&Object.assign(n.devDependencies,{"@types/jsonwebtoken":"^9.0.6"})),"none"!==e.templateEngine){let t="ejs"===e.templateEngine?"^3.1.10":"^3.0.3";if(Object.assign(n.dependencies,{[e.templateEngine]:t}),"ts"===e.language){let t="ejs"===e.templateEngine?"^3.1.5":"^2.0.10";Object.assign(n.devDependencies,{[`@types/${e.templateEngine}`]:t})}}u.writeJsonSync(R().join(t,"package.json"),n,{spaces:2});let y=R().join(t,"src");u.mkdirSync(y),e.language;let f={projectRoot:t,projectName:e.projectName,isTs:"ts"===e.language,language:e.language};await T(r,f,t,n,s),e.validation&&await T(l,f,t,n,s),"rest-swagger"===e.apiType&&await T(m,f,t,n,s),"none"!==e.database&&await T(i,f,t,n,s,{database:e.database}),"jwt"===e.auth&&await T(a,f,t,n,s),"none"!==e.templateEngine&&await T(d,f,t,n,s,{templateEngine:e.templateEngine});let w=s.join("\n")+"\n";u.writeFileSync(R().join(t,".env"),w),u.writeFileSync(R().join(t,".env.example"),w);let v=`import 'dotenv/config';
1
+ import{createRequire as e}from"node:module";let t=e(import.meta.url);import{Command as s}from"commander";import a from"picocolors";import{authPlugin as n}from"@express-tool/plugin-auth";import{ciPlugin as o}from"@express-tool/plugin-ci";import{commonPlugin as r}from"@express-tool/plugin-common";import{databasePlugin as i}from"@express-tool/plugin-database";import{dockerPlugin as p}from"@express-tool/plugin-docker";import{middlewarePlugin as l}from"@express-tool/plugin-middleware";import{qualityPlugin as c}from"@express-tool/plugin-quality";import{swaggerPlugin as g}from"@express-tool/plugin-swagger";import{testingPlugin as m}from"@express-tool/plugin-testing";import{viewsPlugin as u}from"@express-tool/plugin-views";import d from"fs-extra";import y from"prompts";import{z as f}from"zod";import{controllerJs as w,controllerTs as j,routesJs as v,routesTs as h,testJs as x,testTs as S}from"@express-tool/plugin-resource";import $ from"envinfo";var b={317(e){e.exports=t("child_process")}},k={};function C(e){var t=k[e];if(void 0!==t)return t.exports;var s=k[e]={exports:{}};return b[e](s,s.exports,C),s.exports}C.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return C.d(t,{a:t}),t},C.d=(e,t)=>{for(var s in t)C.o(t,s)&&!C.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},C.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var E={};(()=>{C.d(E,{x:()=>B});var e=JSON.parse('{"UU":"@express-tool/cli","rE":"1.0.3"}');let b=e=>console.log(a.blue("ℹ"),e),k=e=>console.log(a.green("✔"),e),D=e=>console.log(a.yellow("⚠"),e),P=e=>console.log(a.red("✖"),e),R=t("path");var T=C.n(R);async function N(e,t,s,a,n,o){b(`Applying plugin: ${e.name}`);let r=await e.apply(t,o);if(r.dependencies&&(a.dependencies={...a.dependencies,...r.dependencies}),r.devDependencies&&(a.devDependencies={...a.devDependencies,...r.devDependencies}),r.files)for(let e of r.files){let t=T().join(s,"src",e.path);d.ensureDirSync(T().dirname(t)),d.writeFileSync(t,e.content),b(` Created ${e.path}`)}if(r.env)for(let[e,t]of Object.entries(r.env))n.push(`${e}=${t}`);if(r.scripts&&(a.scripts={...a.scripts,...r.scripts}),r.commands)for(let e of r.commands){b(` Running command: ${e}`);try{C(317).execSync(e,{cwd:s,stdio:"inherit"})}catch(t){D(` Command failed: ${e}`)}}return r}async function M(e){let t=T().resolve(process.cwd(),e.projectName);d.existsSync(t)&&(P(`Directory ${e.projectName} already exists.`),process.exit(1)),b(`Creating project in ${t}...`),d.mkdirSync(t);let s=[],a={name:e.projectName,version:"0.0.0",private:!0,type:"module",scripts:{dev:"ts"===e.language?"tsx watch src/index.ts":"nodemon --experimental-specifier-resolution=node src/index.js",build:"ts"===e.language?"tsup src/index.ts --format esm --platform node --target es2022 --clean":void 0,start:"ts"===e.language?"node dist/index.js":"node --experimental-specifier-resolution=node src/index.js",..."ts"===e.language?{typecheck:"tsc --noEmit",test:"vitest"}:{}},dependencies:{express:"^5.2.1"},devDependencies:{..."ts"===e.language?{"@types/node":"^25.2.2","@types/express":"^5.0.0","@types/express-serve-static-core":"^5.0.6",tsup:"^8.1.0",tsx:"^4.19.2",typescript:"^5.7.0"}:{}},engines:{node:">=20.0.0"}};if("rest-swagger"===e.apiType&&(Object.assign(a.dependencies,{"swagger-ui-express":"^5.0.0",zod:"^4.3.6","@asteasolutions/zod-to-openapi":"^7.3.0"}),"ts"===e.language&&Object.assign(a.devDependencies,{"@types/swagger-ui-express":"^4.1.6"})),"jwt"===e.auth&&(Object.assign(a.dependencies,{jsonwebtoken:"^9.0.2"}),"ts"===e.language&&Object.assign(a.devDependencies,{"@types/jsonwebtoken":"^9.0.6"})),"none"!==e.templateEngine){let t="ejs"===e.templateEngine?"^3.1.10":"^3.0.3";if(Object.assign(a.dependencies,{[e.templateEngine]:t}),"ts"===e.language){let t="ejs"===e.templateEngine?"^3.1.5":"^2.0.10";Object.assign(a.devDependencies,{[`@types/${e.templateEngine}`]:t})}}d.writeJsonSync(T().join(t,"package.json"),a,{spaces:2});let y=T().join(t,"src");d.mkdirSync(y),e.language;let f={projectRoot:t,projectName:e.projectName,isTs:"ts"===e.language,language:e.language};await N(r,f,t,a,s),e.validation&&await N(l,f,t,a,s),"rest-swagger"===e.apiType&&await N(g,f,t,a,s),"none"!==e.database&&await N(i,f,t,a,s,{database:e.database}),"jwt"===e.auth&&await N(n,f,t,a,s),"none"!==e.templateEngine&&await N(u,f,t,a,s,{templateEngine:e.templateEngine});let w=s.join("\n")+"\n";d.writeFileSync(T().join(t,".env"),w),d.writeFileSync(T().join(t,".env.example"),w);let j=`import 'dotenv/config';
2
2
  import express${"ts"===e.language?", { Request, Response }":""} from 'express';
3
3
  import cors from 'cors';
4
4
  import helmet from 'helmet';
@@ -6,15 +6,16 @@ import path from 'path';
6
6
  import { fileURLToPath } from 'url';
7
7
  import pinoHttp from 'pino-http';
8
8
  import { rateLimit } from 'express-rate-limit';
9
- import { logger } from './utils/logger';
10
- import { errorHandler } from './middleware/errorHandler';
11
- ${"mongodb"===e.database?"import { connectDB } from './lib/db';":""}
9
+ import { logger } from './utils/logger${"ts"===e.language?"":".js"}';
10
+ import { errorHandler } from './middleware/errorHandler${"ts"===e.language?"":".js"}';
11
+ ${"mongodb"===e.database?`import { connectDB } from './lib/db${"ts"===e.language?"":".js"}';`:""}
12
12
 
13
13
  const __filename = fileURLToPath(import.meta.url);
14
14
  const __dirname = path.dirname(__filename);
15
15
 
16
- ${"jwt"===e.auth?"import { authRouter } from './routes/auth';\nimport { authenticateToken } from './middleware/auth';":""}
17
- ${"rest-swagger"===e.apiType?"import { swaggerRouter } from './docs/index';":""}
16
+ ${"jwt"===e.auth?`import { authRouter } from './routes/auth${"ts"===e.language?"":".js"}';
17
+ import { authenticateToken } from './middleware/auth${"ts"===e.language?"":".js"}';`:""}
18
+ ${"rest-swagger"===e.apiType?`import { swaggerRouter } from './docs/index${"ts"===e.language?"":".js"}';`:""}
18
19
 
19
20
  const app = express();
20
21
  const port = process.env.PORT || 3000;
@@ -41,20 +42,20 @@ app.set('view engine', '${e.templateEngine}');
41
42
  app.use(express.static(path.join(__dirname, 'public')));
42
43
  `:""}
43
44
 
44
- `;"rest-swagger"===e.apiType&&(v+=`app.use('/docs', swaggerRouter);
45
- `),"jwt"===e.auth&&(v+=`app.use('/auth', authRouter);
46
- `),v+=`app.get('/', (req${"ts"===e.language?": Request":""}, res${"ts"===e.language?": Response":""}) => {
45
+ `;"rest-swagger"===e.apiType&&(j+=`app.use('/docs', swaggerRouter);
46
+ `),"jwt"===e.auth&&(j+=`app.use('/auth', authRouter);
47
+ `),j+=`app.get('/', (req${"ts"===e.language?": Request":""}, res${"ts"===e.language?": Response":""}) => {
47
48
  ${"none"!==e.templateEngine?`res.render('index', { title: 'Express App', message: 'Hello from ${e.templateEngine.toUpperCase()}!' });`:"res.json({ status: 'ok', timestamp: new Date().toISOString() });"}
48
49
  });
49
50
 
50
51
  app.get('/health', (req${"ts"===e.language?": Request":""}, res${"ts"===e.language?": Response":""}) => {
51
52
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
52
53
  });
53
- `,"jwt"===e.auth&&(v+=`
54
+ `,"jwt"===e.auth&&(j+=`
54
55
  app.get('/protected', authenticateToken, (req${"ts"===e.language?": Request":""}, res${"ts"===e.language?": Response":""}) => {
55
56
  res.json({ message: 'This is a protected route', user: (req as any).user });
56
57
  });
57
- `),v+=`
58
+ `),j+=`
58
59
  app.use(errorHandler);
59
60
 
60
61
  // Export app for testing
@@ -78,12 +79,12 @@ if (process.env.NODE_ENV !== 'test') {
78
79
  process.on('SIGTERM', shutdown);
79
80
  process.on('SIGINT', shutdown);
80
81
  }
81
- `;let j="ts"===e.language?"index.ts":"index.js";u.writeFileSync(R().join(y,j),v),await T(g,f,t,n,s),e.linting&&await T(c,f,t,n,s),await T(p,f,t,n,s,{packageManager:e.packageManager,database:e.database}),await T(o,f,t,n,s,{packageManager:e.packageManager}),"ts"===e.language&&u.writeJsonSync(R().join(t,"tsconfig.json"),{compilerOptions:{target:"ES2022",module:"NodeNext",moduleResolution:"NodeNext",outDir:"./dist",rootDir:"./src",strict:!0,esModuleInterop:!0,skipLibCheck:!0,forceConsistentCasingInFileNames:!0},include:["src/**/*"]},{spaces:2});let h=`node_modules
82
+ `;let v="ts"===e.language?"index.ts":"index.js";d.writeFileSync(T().join(y,v),j),await N(m,f,t,a,s),e.linting&&await N(c,f,t,a,s),await N(p,f,t,a,s,{packageManager:e.packageManager,database:e.database}),await N(o,f,t,a,s,{packageManager:e.packageManager}),"ts"===e.language&&d.writeJsonSync(T().join(t,"tsconfig.json"),{compilerOptions:{target:"ES2022",lib:["ES2022"],module:"ESNext",moduleResolution:"bundler",rootDir:"./src",outDir:"./dist",strict:!0,esModuleInterop:!0,skipLibCheck:!0,declaration:!0,sourceMap:!0,noEmit:!0},include:["src/**/*"]},{spaces:2});let h=`node_modules
82
83
  dist
83
84
  .env
84
85
  .DS_Store
85
- `;u.writeFileSync(R().join(t,".gitignore"),h),u.writeJsonSync(R().join(t,"package.json"),n,{spaces:2}),b("Installing dependencies...");try{let s=(e.packageManager,"install");k(317).execSync(`${e.packageManager} ${s}`,{cwd:t,stdio:"inherit"})}catch(e){N("Failed to install dependencies. Please run install manually.")}C(`Project ${e.projectName} created successfully!`),console.log(`
86
+ `;d.writeFileSync(T().join(t,".gitignore"),h),d.writeJsonSync(T().join(t,"package.json"),a,{spaces:2}),b("Installing dependencies...");try{let s=(e.packageManager,"install");C(317).execSync(`${e.packageManager} ${s}`,{cwd:t,stdio:"inherit"})}catch(e){D("Failed to install dependencies. Please run install manually.")}k(`Project ${e.projectName} created successfully!`),console.log(`
86
87
  Next steps:`),console.log(` cd ${e.projectName}`),console.log(` ${e.packageManager} run dev`),"prisma-postgres"===e.database&&console.log(` ${e.packageManager} run db:init (To setup Prisma Postgres Managed DB)`),console.log(`
87
88
  Happy Coding! 🚀
88
- `)}let L=f.object({projectName:f.string().min(1).regex(/^[a-z0-9-]+$/,"Project name must be kebab-case (lowercase letters, numbers, and hyphens)"),language:f.enum(["ts","js"]),architecture:f.enum(["feature","mvc"]),apiType:f.enum(["rest","rest-swagger"]),database:f.enum(["postgresql","prisma-postgres","mysql","mongodb","none"]),auth:f.enum(["jwt","none"]),templateEngine:f.enum(["ejs","pug","none"]),packageManager:f.enum(["npm","pnpm","yarn","bun"]),linting:f.boolean(),validation:f.boolean()});async function _(){return await y([{type:"text",name:"projectName",message:"What is your project name?",initial:"my-express-app",validate:e=>{let t=L.shape.projectName.safeParse(e);return!!t.success||t.error.issues[0].message}},{type:"select",name:"language",message:"Select language",choices:[{title:"TypeScript (Recommended)",value:"ts"},{title:"JavaScript",value:"js"}],initial:0},{type:"select",name:"packageManager",message:"Select package manager",choices:[{title:"pnpm (Recommended)",value:"pnpm"},{title:"npm",value:"npm"},{title:"Yarn",value:"yarn"},{title:"Bun",value:"bun"}],initial:0},{type:"select",name:"architecture",message:"Select architecture",choices:[{title:"Feature-based (Recommended)",value:"feature",description:"Modules grouped by feature (e.g., users, posts)"},{title:"MVC",value:"mvc",description:"Classic Model-View-Controller structure"}],initial:0},{type:"select",name:"apiType",message:"Select API type",choices:[{title:"REST API + Swagger",value:"rest-swagger"},{title:"REST API (Basic)",value:"rest"}],initial:0},{type:"select",name:"database",message:"Select database",choices:[{title:"PostgreSQL (Prisma)",value:"postgresql"},{title:"PostgreSQL (Prisma Postgres Managed)",value:"prisma-postgres"},{title:"MySQL (Prisma)",value:"mysql"},{title:"MongoDB (Mongoose)",value:"mongodb"},{title:"None",value:"none"}],initial:0},{type:"select",name:"auth",message:"Select authentication",choices:[{title:"JWT (JSON Web Token)",value:"jwt"},{title:"None",value:"none"}],initial:0},{type:"select",name:"templateEngine",message:"Select template engine",choices:[{title:"EJS (Embedded JavaScript)",value:"ejs"},{title:"Pug (Jade)",value:"pug"},{title:"None (API only)",value:"none"}],initial:0},{type:"confirm",name:"linting",message:"Do you want to include code quality tools (Linting & Formatting)?",initial:!0},{type:"confirm",name:"validation",message:"Do you want to include runtime validation (Zod)?",initial:!0}],{onCancel:()=>{D("Operation cancelled"),process.exit(1)}})}let O=new s("init").description("Initialize a new Express.js project").action(async()=>{let e=await _();await M(e)});async function I(e){let t=process.cwd(),s=R().join(t,"package.json");u.existsSync(s)||(D("No package.json found. Are you in the root of the project?"),process.exit(1));let n=u.readJsonSync(s),a=u.existsSync(R().join(t,"tsconfig.json"))||n.devDependencies&&n.devDependencies.typescript||n.dependencies&&n.dependencies.typescript,o=a?"ts":"js",r=R().join(t,"src"),i=R().join(r,"controllers"),p=R().join(r,"routes"),l=R().join(t,"test");u.ensureDirSync(i),u.ensureDirSync(p),u.ensureDirSync(l);let c=e.charAt(0).toUpperCase()+e.slice(1),m=R().join(i,`${e.toLowerCase()}.controller.${o}`);u.existsSync(m)?N(`Controller ${m} already exists. Skipping.`):(u.writeFileSync(m,a?v(c):w(c)),b(`Created controller: src/controllers/${e.toLowerCase()}.controller.${o}`));let g=R().join(p,`${e.toLowerCase()}.routes.${o}`);u.existsSync(g)?N(`Route ${g} already exists. Skipping.`):(u.writeFileSync(g,a?h(c):j(c)),b(`Created route: src/routes/${e.toLowerCase()}.routes.${o}`));let d=R().join(l,`${e.toLowerCase()}.test.${o}`);u.existsSync(d)?N(`Test ${d} already exists. Skipping.`):(u.writeFileSync(d,a?S(c):x(c)),b(`Created test: test/${e.toLowerCase()}.test.${o}`)),C(`Feature ${e} generated successfully!`),b(`
89
- Don't forget to register the route in src/index.${o}:`),b(`import { ${e.toLowerCase()}Router } from './routes/${e.toLowerCase()}.routes';`),b(`app.use('/${e.toLowerCase()}s', ${e.toLowerCase()}Router);`)}let F=new s().name("generate").alias("g").description("Generate a new feature (controller, routes, test)").argument("<name>","Name of the feature (e.g. users, blogs)").action(async e=>{await I(e)}),q=new s().name("info").description("Print debugging information about your environment").action(async()=>{console.log(" System:"),await $.run({System:["OS","CPU","Memory","Shell"],Binaries:["Node","Yarn","npm","pnpm"],Utilities:["Git"],IDEs:["VSCode"],Browsers:["Chrome","Edge","Firefox","Safari"],npmPackages:["typescript","express","express-tool"]},{console:!0,showNotFound:!0})}),J=t("https");var H=k.n(J);let U=new s().name("upgrade").description("Check for updates").action(async()=>{b("Checking for updates...");let t=e.rE,s=e.UU;try{var n;let e=await (n=s,new Promise(e=>{H().get(`https://registry.npmjs.org/${n}/latest`,t=>{let s="";t.on("data",e=>s+=e),t.on("end",()=>{try{let t=JSON.parse(s);e(t.version)}catch{e(null)}})}).on("error",()=>e(null))}));if(!e)return void N("Could not fetch latest version info.");e!==t?(b(`New version available: ${e} (current: ${t})`),b(`Run 'npm install -g ${s}' to update.`)):C("You are using the latest version.")}catch(e){D("Failed to check for updates.")}}),{rE:A}=e,B=new s;B.name("express-tool").description("Production-grade CLI for Express.js applications").version(A),B.hook("preAction",()=>{b("Welcome to express-tool CLI")}),B.addCommand(O),B.addCommand(F),B.addCommand(q),B.addCommand(U)})();var N=E.x;export{N as cli};
89
+ `)}let L=f.object({projectName:f.string().min(1).regex(/^[a-z0-9-]+$/,"Project name must be kebab-case (lowercase letters, numbers, and hyphens)"),language:f.enum(["ts","js"]),architecture:f.enum(["feature","mvc"]),apiType:f.enum(["rest","rest-swagger"]),database:f.enum(["postgresql","prisma-postgres","mysql","mongodb","none"]),auth:f.enum(["jwt","none"]),templateEngine:f.enum(["ejs","pug","none"]),packageManager:f.enum(["npm","pnpm","yarn","bun"]),linting:f.boolean(),validation:f.boolean()});async function _(){return await y([{type:"text",name:"projectName",message:"What is your project name?",initial:"my-express-app",validate:e=>{let t=L.shape.projectName.safeParse(e);return!!t.success||t.error.issues[0].message}},{type:"select",name:"language",message:"Select language",choices:[{title:"TypeScript (Recommended)",value:"ts"},{title:"JavaScript",value:"js"}],initial:0},{type:"select",name:"packageManager",message:"Select package manager",choices:[{title:"pnpm (Recommended)",value:"pnpm"},{title:"npm",value:"npm"},{title:"Yarn",value:"yarn"},{title:"Bun",value:"bun"}],initial:0},{type:"select",name:"architecture",message:"Select architecture",choices:[{title:"Feature-based (Recommended)",value:"feature",description:"Modules grouped by feature (e.g., users, posts)"},{title:"MVC",value:"mvc",description:"Classic Model-View-Controller structure"}],initial:0},{type:"select",name:"apiType",message:"Select API type",choices:[{title:"REST API + Swagger",value:"rest-swagger"},{title:"REST API (Basic)",value:"rest"}],initial:0},{type:"select",name:"database",message:"Select database",choices:[{title:"PostgreSQL (Prisma)",value:"postgresql"},{title:"PostgreSQL (Prisma Postgres Managed)",value:"prisma-postgres"},{title:"MySQL (Prisma)",value:"mysql"},{title:"MongoDB (Mongoose)",value:"mongodb"},{title:"None",value:"none"}],initial:0},{type:"select",name:"auth",message:"Select authentication",choices:[{title:"JWT (JSON Web Token)",value:"jwt"},{title:"None",value:"none"}],initial:0},{type:"select",name:"templateEngine",message:"Select template engine",choices:[{title:"EJS (Embedded JavaScript)",value:"ejs"},{title:"Pug (Jade)",value:"pug"},{title:"None (API only)",value:"none"}],initial:0},{type:"confirm",name:"linting",message:"Do you want to include code quality tools (Linting & Formatting)?",initial:!0},{type:"confirm",name:"validation",message:"Do you want to include runtime validation (Zod)?",initial:!0}],{onCancel:()=>{P("Operation cancelled"),process.exit(1)}})}let O=new s("init").description("Initialize a new Express.js project").action(async()=>{let e=await _();await M(e)});async function I(e){let t=process.cwd(),s=T().join(t,"package.json");d.existsSync(s)||(P("No package.json found. Are you in the root of the project?"),process.exit(1));let a=d.readJsonSync(s),n=d.existsSync(T().join(t,"tsconfig.json"))||a.devDependencies&&a.devDependencies.typescript||a.dependencies&&a.dependencies.typescript,o=n?"ts":"js",r=T().join(t,"src"),i=T().join(r,"controllers"),p=T().join(r,"routes"),l=T().join(t,"test");d.ensureDirSync(i),d.ensureDirSync(p),d.ensureDirSync(l);let c=e.charAt(0).toUpperCase()+e.slice(1),g=T().join(i,`${e.toLowerCase()}.controller.${o}`);d.existsSync(g)?D(`Controller ${g} already exists. Skipping.`):(d.writeFileSync(g,n?j(c):w(c)),b(`Created controller: src/controllers/${e.toLowerCase()}.controller.${o}`));let m=T().join(p,`${e.toLowerCase()}.routes.${o}`);d.existsSync(m)?D(`Route ${m} already exists. Skipping.`):(d.writeFileSync(m,n?h(c):v(c)),b(`Created route: src/routes/${e.toLowerCase()}.routes.${o}`));let u=T().join(l,`${e.toLowerCase()}.test.${o}`);d.existsSync(u)?D(`Test ${u} already exists. Skipping.`):(d.writeFileSync(u,n?S(c):x(c)),b(`Created test: test/${e.toLowerCase()}.test.${o}`)),k(`Feature ${e} generated successfully!`),b(`
90
+ Don't forget to register the route in src/index.${o}:`),b(`import { ${e.toLowerCase()}Router } from './routes/${e.toLowerCase()}.routes${n?"":".js"}';`),b(`app.use('/${e.toLowerCase()}s', ${e.toLowerCase()}Router);`)}let q=new s().name("generate").alias("g").description("Generate a new feature (controller, routes, test)").argument("<name>","Name of the feature (e.g. users, blogs)").action(async e=>{await I(e)}),F=new s().name("info").description("Print debugging information about your environment").action(async()=>{console.log(" System:"),await $.run({System:["OS","CPU","Memory","Shell"],Binaries:["Node","Yarn","npm","pnpm"],Utilities:["Git"],IDEs:["VSCode"],Browsers:["Chrome","Edge","Firefox","Safari"],npmPackages:["typescript","express","express-tool"]},{console:!0,showNotFound:!0})}),J=t("https");var H=C.n(J);let U=new s().name("upgrade").description("Check for updates").action(async()=>{b("Checking for updates...");let t=e.rE,s=e.UU;try{var a;let e=await (a=s,new Promise(e=>{H().get(`https://registry.npmjs.org/${a}/latest`,t=>{let s="";t.on("data",e=>s+=e),t.on("end",()=>{try{let t=JSON.parse(s);e(t.version)}catch{e(null)}})}).on("error",()=>e(null))}));if(!e)return void D("Could not fetch latest version info.");e!==t?(b(`New version available: ${e} (current: ${t})`),b(`Run 'npm install -g ${s}' to update.`)):k("You are using the latest version.")}catch(e){P("Failed to check for updates.")}}),{rE:A}=e,B=new s;B.name("express-tool").description("Production-grade CLI for Express.js applications").version(A),B.hook("preAction",()=>{b("Welcome to express-tool CLI")}),B.addCommand(O),B.addCommand(q),B.addCommand(F),B.addCommand(U)})();var D=E.x;export{D as cli};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@express-tool/cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Production-grade CLI for Express.js applications",
5
5
  "keywords": [
6
6
  "express",
@@ -44,12 +44,12 @@
44
44
  "@express-tool/plugin-auth": "1.0.2",
45
45
  "@express-tool/plugin-swagger": "1.0.2",
46
46
  "@express-tool/core": "1.0.2",
47
- "@express-tool/plugin-middleware": "1.0.2",
48
- "@express-tool/plugin-common": "1.0.2",
49
- "@express-tool/plugin-testing": "1.0.2",
50
47
  "@express-tool/plugin-database": "1.0.2",
51
- "@express-tool/plugin-docker": "1.0.2",
52
48
  "@express-tool/plugin-views": "1.0.2",
49
+ "@express-tool/plugin-middleware": "1.0.2",
50
+ "@express-tool/plugin-common": "1.0.3",
51
+ "@express-tool/plugin-docker": "1.0.2",
52
+ "@express-tool/plugin-testing": "1.0.2",
53
53
  "@express-tool/plugin-ci": "1.0.2",
54
54
  "@express-tool/plugin-quality": "1.0.2",
55
55
  "@express-tool/plugin-resource": "1.0.2"
@@ -59,16 +59,16 @@
59
59
  "@rsbuild/plugin-type-check": "^1.3.3",
60
60
  "@types/envinfo": "^7.8.4",
61
61
  "@types/fs-extra": "^11.0.4",
62
- "@types/node": "^25.2.1",
62
+ "@types/node": "^25.2.2",
63
63
  "@types/prompts": "^2.4.9",
64
- "eslint": "^8.57.1",
64
+ "eslint": "^10.0.0",
65
65
  "typescript": "^5.9.3"
66
66
  },
67
67
  "scripts": {
68
68
  "build": "rsbuild build && tsc -b --force --emitDeclarationOnly",
69
69
  "dev": "rsbuild build --watch",
70
70
  "start": "node dist/bin.js",
71
- "lint": "eslint . --max-warnings 0",
71
+ "lint": "eslint .",
72
72
  "test": "echo \"Error: no test specified\" && exit 1"
73
73
  }
74
74
  }