sprygen 1.0.3 → 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 +8 -0
- package/dist/cli.js +14 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,6 +69,14 @@ Useful for modifying existing projects. Scaffolds Sprygen's robust JWT authentic
|
|
|
69
69
|
sprygen generate-auth
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
### 4. Update Sprygen
|
|
73
|
+
|
|
74
|
+
Ensure you are always running the latest version with the newest features and bug fixes by using the built-in update command.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
sprygen update
|
|
78
|
+
```
|
|
79
|
+
|
|
72
80
|
## 🏗️ Project Architecture
|
|
73
81
|
|
|
74
82
|
Applications generated by Sprygen follow standard Spring Boot best practices:
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
3
|
-
`):process.stdout.write(
|
|
4
|
-
`)}),console.log();let e=" Spring Boot Project Generator",a=`v${t}`;process.stdout.write(
|
|
5
|
-
`),console.log(
|
|
6
|
-
`+
|
|
2
|
+
"use strict";var ge=Object.create;var B=Object.defineProperty;var ue=Object.getOwnPropertyDescriptor;var je=Object.getOwnPropertyNames;var fe=Object.getPrototypeOf,he=Object.prototype.hasOwnProperty;var M=(t,e)=>()=>(t&&(e=t(t=0)),e);var ye=(t,e)=>{for(var a in e)B(t,a,{get:e[a],enumerable:!0})},we=(t,e,a,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of je(e))!he.call(t,i)&&i!==a&&B(t,i,{get:()=>e[i],enumerable:!(r=ue(e,i))||r.enumerable});return t};var j=(t,e,a)=>(a=t!=null?ge(fe(t)):{},we(e||!t||!t.__esModule?B(a,"default",{value:t,enumerable:!0}):a,t));function T(t="1.0.0"){console.log(),ve.forEach((r,i)=>{i<2?process.stdout.write(g.default.hex(v.soft)(r)+`
|
|
3
|
+
`):process.stdout.write(g.default.hex(v.bright)(r)+`
|
|
4
|
+
`)}),console.log();let e=" Spring Boot Project Generator",a=`v${t}`;process.stdout.write(g.default.hex(v.base)(e)+g.default.dim(" \xB7 ")+g.default.dim(a)+`
|
|
5
|
+
`),console.log(g.default.hex(v.soft)(" "+"\u2500".repeat(62))),console.log()}function S(t){console.log(`
|
|
6
|
+
`+g.default.hex(v.bright)(" \u25C6 ")+g.default.bold.white(t))}function be(){console.log(g.default.hex(v.soft)(" "+"\u2500".repeat(62)))}var g,v,ve,l,x=M(()=>{"use strict";g=j(require("chalk")),v={bright:"#3fb950",base:"#2da44e",dim:"#26913d",soft:"#156e2e"},ve=[" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557"," \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551"," \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551"," \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u255A\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551"," \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551"," \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D"];l={info:t=>console.log(` ${g.default.hex(v.base)("\xB7")} ${g.default.white(t)}`),success:t=>console.log(` ${g.default.hex(v.bright)("+")} ${g.default.white(t)}`),warn:t=>console.log(` ${g.default.yellow("!")} ${g.default.yellow(t)}`),error:t=>console.error(` ${g.default.red("\xD7")} ${g.default.red(t)}`),title:t=>{console.log(),console.log(g.default.hex(v.bright)(" \u25C6 ")+g.default.bold.white(t)),console.log()},file:t=>console.log(` ${g.default.hex(v.soft)("write")} ${g.default.dim(t)}`),step:(t,e,a)=>console.log(g.default.hex(v.soft)(` [${t}/${e}]`)+" "+g.default.white(a)),done:()=>{console.log(),be(),console.log(` ${g.default.hex(v.bright)("+")} ${g.default.bold.white("Done.")}`),console.log()}}});var F={};ye(F,{copyTemplate:()=>Z,renderTemplate:()=>V,writeGeneratedFile:()=>y});async function V(t,e){let a=await P.default.readFile(t,"utf-8");return _.default.render(a,e,{async:!1})}async function y(t,e,a){let r=await V(e,a);await P.default.ensureDir(E.default.dirname(t)),await P.default.writeFile(t,r,"utf-8"),l.file(t)}async function Z(t,e,a){let r=await P.default.readdir(t,{withFileTypes:!0});for(let i of r){let n=E.default.join(t,i.name),o=xe(i.name,a),c=o.endsWith(".ejs")?o.slice(0,-4):o,p=E.default.join(e,c);i.isDirectory()?(await P.default.ensureDir(p),await Z(n,p,a)):i.name.endsWith(".ejs")?await y(p,n,a):(await P.default.copy(n,p),l.file(p))}}function xe(t,e){return t.replace(/\{\{(\w+)\}\}/g,(a,r)=>String(e[r]??`{{${r}}}`))}var _,P,E,C=M(()=>{"use strict";_=j(require("ejs")),P=j(require("fs-extra")),E=j(require("path"));x()});var de=require("commander");var R=j(require("inquirer"));var s=j(require("path")),u=j(require("fs-extra")),H=j(require("ora")),Y=j(require("axios")),Q=j(require("adm-zip"));x();var I=class{templatesDir;constructor(){let e=[s.default.resolve(__dirname,"../../templates/project"),s.default.resolve(__dirname,"../templates/project")],a=e.find(r=>u.default.existsSync(r));if(!a)throw new Error(`Could not locate templates directory. Tried:
|
|
7
7
|
${e.join(`
|
|
8
|
-
`)}`);this.templatesDir=a}async generate(e){let a=s.default.resolve(process.cwd(),e.projectName);if(await u.default.pathExists(a))throw new Error(`Directory "${e.projectName}" already exists. Please choose a different project name or remove the existing directory.`);let r=e.projectName.toLowerCase().replace(/[^a-z0-9-]/g,"-"),i=e.packageName.split(".").slice(0,-1).join(".")||e.packageName;
|
|
8
|
+
`)}`);this.templatesDir=a}async generate(e){let a=s.default.resolve(process.cwd(),e.projectName);if(await u.default.pathExists(a))throw new Error(`Directory "${e.projectName}" already exists. Please choose a different project name or remove the existing directory.`);let r=e.projectName.toLowerCase().replace(/[^a-z0-9-]/g,"-"),i=e.packageName.split(".").slice(0,-1).join(".")||e.packageName;l.title(`Generating Spring Boot project: ${e.projectName}`);let n=(0,H.default)({text:"Downloading from Spring Initializr\u2026",color:"cyan"}).start();try{let o="data-jpa,security,validation,lombok";o+=",web",e.database==="mysql"&&(o+=",mysql"),e.database==="postgresql"&&(o+=",postgresql"),e.database==="h2"&&(o+=",h2"),e.modules.includes("Mail")&&(o+=",mail");let c=e.authStrategy==="session"&&e.projectType==="fullstack";c&&(o+=",thymeleaf");let p=e.buildTool==="maven"?"maven-project":"gradle-project",m=new URL("https://start.spring.io/starter.zip");m.searchParams.append("type",p),m.searchParams.append("language","java"),m.searchParams.append("baseDir",e.projectName),m.searchParams.append("groupId",i),m.searchParams.append("artifactId",r),m.searchParams.append("name",e.projectName),m.searchParams.append("description",e.description),m.searchParams.append("packageName",e.packageName),m.searchParams.append("packaging","jar"),m.searchParams.append("javaVersion",e.javaVersion),m.searchParams.append("dependencies",o);let d=await(0,Y.default)({url:m.toString(),method:"GET",responseType:"arraybuffer"});n.text="Extracting project scaffold\u2026";let h=s.default.resolve(process.cwd(),`${e.projectName}-tmp.zip`);await u.default.writeFile(h,d.data),new Q.default(h).extractAllTo(process.cwd(),!0),await u.default.unlink(h),n.text="Patching dependencies\u2026",await this.patchDependencies(a,e,c),n.text="Writing source files\u2026";let $=this.buildContext(e,a,r,i),k=s.default.join(a,"src/main/java",e.packagePath),N=s.default.join(a,"src/main/resources");await u.default.ensureDir(k),await u.default.ensureDir(N);let G=s.default.join(N,"application.properties");await u.default.pathExists(G)&&await u.default.unlink(G),await this.generateJavaSources(k,$,e),await this.generateResources(N,$,e),e.projectType==="fullstack"&&(n.text="Writing frontend files\u2026",await this.generateFrontend(a,$,e,c)),n.succeed(`Project "${e.projectName}" scaffolded successfully!`)}catch(o){throw n.fail("Project generation failed."),o}this.printSuccessMessage(e)}async patchDependencies(e,a,r){let i=a.modules.includes("Swagger");if(a.buildTool==="maven"){let n=s.default.join(e,"pom.xml"),o=await u.default.readFile(n,"utf8"),c=a.authStrategy==="jwt"?`
|
|
9
9
|
<!-- JWT Dependencies -->
|
|
10
10
|
<dependency>
|
|
11
11
|
<groupId>io.jsonwebtoken</groupId>
|
|
@@ -23,7 +23,7 @@ ${e.join(`
|
|
|
23
23
|
<artifactId>jjwt-jackson</artifactId>
|
|
24
24
|
<version>0.11.5</version>
|
|
25
25
|
<scope>runtime</scope>
|
|
26
|
-
</dependency>`:"",
|
|
26
|
+
</dependency>`:"",p=i?`
|
|
27
27
|
<!-- Swagger/OpenAPI -->
|
|
28
28
|
<dependency>
|
|
29
29
|
<groupId>org.springdoc</groupId>
|
|
@@ -34,7 +34,7 @@ ${e.join(`
|
|
|
34
34
|
<dependency>
|
|
35
35
|
<groupId>org.thymeleaf.extras</groupId>
|
|
36
36
|
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
|
|
37
|
-
</dependency>`:"";o=o.replace("</dependencies>",`${c}${
|
|
37
|
+
</dependency>`:"";o=o.replace("</dependencies>",`${c}${p}${m}
|
|
38
38
|
</dependencies>`),await u.default.writeFile(n,o,"utf8")}else{let n=s.default.join(e,"build.gradle"),o=i?`
|
|
39
39
|
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'`:"",c=r?`
|
|
40
40
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'`:"",m=`
|
|
@@ -43,13 +43,13 @@ dependencies {${a.authStrategy==="jwt"?`
|
|
|
43
43
|
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
|
|
44
44
|
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'`:""}${o}${c}
|
|
45
45
|
}
|
|
46
|
-
`;await u.default.appendFile(n,m,"utf8")}}buildContext(e,a,r,i){let n=this.toPascalCase(e.projectName.replace(/[^a-zA-Z0-9]/g,""))+"Application",o=this.getDbDependencies(e.database),c=this.getDbConfig(e.database,r);return{projectName:e.projectName,projectNamePascal:n.replace("Application",""),artifactId:r,groupId:i,packageName:e.packageName,packagePath:e.packagePath,database:e.database,buildTool:e.buildTool,mainClassName:n,javaVersion:e.javaVersion,springBootVersion:e.springBootVersion,description:e.description,hasSwagger:e.modules.includes("Swagger"),hasMail:e.modules.includes("Mail"),hasLogging:e.modules.includes("Logging"),isJwtAuth:e.authStrategy==="jwt",isSessionAuth:e.authStrategy==="session",isFullstack:e.projectType==="fullstack",isApiOnly:e.projectType==="api",dbDependency:o.dependency,dbDriverClass:o.driverClass,dbConfig:c,outputDir:a,modules:e.modules,year:new Date().getFullYear()}}async generateJavaSources(e,a,r){let{writeGeneratedFile:i}=await Promise.resolve().then(()=>(
|
|
46
|
+
`;await u.default.appendFile(n,m,"utf8")}}buildContext(e,a,r,i){let n=this.toPascalCase(e.projectName.replace(/[^a-zA-Z0-9]/g,""))+"Application",o=this.getDbDependencies(e.database),c=this.getDbConfig(e.database,r);return{projectName:e.projectName,projectNamePascal:n.replace("Application",""),artifactId:r,groupId:i,packageName:e.packageName,packagePath:e.packagePath,database:e.database,buildTool:e.buildTool,mainClassName:n,javaVersion:e.javaVersion,springBootVersion:e.springBootVersion,description:e.description,hasSwagger:e.modules.includes("Swagger"),hasMail:e.modules.includes("Mail"),hasLogging:e.modules.includes("Logging"),isJwtAuth:e.authStrategy==="jwt",isSessionAuth:e.authStrategy==="session",isFullstack:e.projectType==="fullstack",isApiOnly:e.projectType==="api",dbDependency:o.dependency,dbDriverClass:o.driverClass,dbConfig:c,outputDir:a,modules:e.modules,year:new Date().getFullYear()}}async generateJavaSources(e,a,r){let{writeGeneratedFile:i}=await Promise.resolve().then(()=>(C(),F)),n=s.default.join(this.templatesDir,"java"),o=a,c=[["entity/User.java","entity/User.java.ejs"],["entity/Role.java","entity/Role.java.ejs"],["repository/UserRepository.java","repository/UserRepository.java.ejs"],["service/UserService.java","service/UserService.java.ejs"],["controller/AuthController.java","controller/AuthController.java.ejs"],["controller/UserController.java","controller/UserController.java.ejs"],["controller/HomeController.java","controller/HomeController.java.ejs"],["controller/ProfileController.java","controller/ProfileController.java.ejs"],["controller/AdminController.java","controller/AdminController.java.ejs"],["config/CorsConfig.java","config/CorsConfig.java.ejs"],["dto/AuthRequest.java","dto/AuthRequest.java.ejs"],["dto/AuthResponse.java","dto/AuthResponse.java.ejs"],["dto/RegisterRequest.java","dto/RegisterRequest.java.ejs"],["dto/ProfileUpdateRequest.java","dto/ProfileUpdateRequest.java.ejs"],["dto/UserDto.java","dto/UserDto.java.ejs"]];r.authStrategy==="jwt"?c.push(["service/JwtService.java","service/JwtService.java.ejs"],["security/JwtAuthFilter.java","security/JwtAuthFilter.java.ejs"],["security/UserDetailsServiceImpl.java","security/UserDetailsServiceImpl.java.ejs"],["config/SecurityConfig.java","config/SecurityConfig.java.ejs"]):c.push(["security/UserDetailsServiceImpl.java","security/UserDetailsServiceImpl.java.ejs"],["config/SecurityConfig.java","config/SecurityConfigSession.java.ejs"]),r.modules.includes("Swagger")&&c.push(["config/SwaggerConfig.java","config/SwaggerConfig.java.ejs"]);for(let[p,m]of c){let d=s.default.join(n,m);await u.default.pathExists(d)?await i(s.default.join(e,p),d,o):l.warn(`Template not found, skipping: ${m}`)}}async generateResources(e,a,r){let{writeGeneratedFile:i}=await Promise.resolve().then(()=>(C(),F)),n=s.default.join(this.templatesDir,"resources"),o=a;await i(s.default.join(e,"application.yml"),s.default.join(n,"application.yml.ejs"),o),r.modules.includes("Logging")&&await i(s.default.join(e,"logback-spring.xml"),s.default.join(n,"logback-spring.xml.ejs"),o)}async generateFrontend(e,a,r,i){let{writeGeneratedFile:n}=await Promise.resolve().then(()=>(C(),F)),o=a;if(i){let c=s.default.join(this.templatesDir,"thymeleaf"),p=s.default.join(e,"src/main/resources/templates");await u.default.ensureDir(p),await u.default.ensureDir(s.default.join(p,"admin"));let m=[["login.html","login.html.ejs"],["register.html","register.html.ejs"],["dashboard.html","dashboard.html.ejs"],["profile.html","profile.html.ejs"],["admin/users.html","admin/users.html.ejs"]];for(let[h,b]of m)await n(s.default.join(p,h),s.default.join(c,b),o);let d=s.default.join(e,"src/main/resources/static/css");await u.default.ensureDir(d),await u.default.copyFile(s.default.join(this.templatesDir,"static/css/style.css"),s.default.join(d,"style.css"))}else{let c=s.default.join(this.templatesDir,"static"),p=s.default.join(e,"src/main/resources/static");await u.default.ensureDir(s.default.join(p,"css")),await u.default.ensureDir(s.default.join(p,"js"));let m=[["index.html","index.html.ejs"],["login.html","login.html.ejs"],["register.html","register.html.ejs"],["dashboard.html","dashboard.html.ejs"],["profile.html","profile.html.ejs"],["admin.html","admin.html.ejs"]];for(let[d,h]of m){let b=s.default.join(c,h);await u.default.pathExists(b)&&await n(s.default.join(p,d),b,o)}await n(s.default.join(p,"js/nav.js"),s.default.join(c,"js/nav.js.ejs"),o),await u.default.copyFile(s.default.join(c,"js/auth.js"),s.default.join(p,"js/auth.js")),await u.default.copyFile(s.default.join(c,"js/ui.js"),s.default.join(p,"js/ui.js")),await u.default.copyFile(s.default.join(c,"css/style.css"),s.default.join(p,"css/style.css"))}}toPascalCase(e){return e.charAt(0).toUpperCase()+e.slice(1)}getDbDependencies(e){switch(e){case"mysql":return{dependency:"mysql",driverClass:"com.mysql.cj.jdbc.Driver"};case"postgresql":return{dependency:"postgresql",driverClass:"org.postgresql.Driver"};default:return{dependency:"h2",driverClass:"org.h2.Driver"}}}getDbConfig(e,a){switch(e){case"mysql":return{url:`jdbc:mysql://localhost:3306/${a}?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true`,username:"root",password:"YOUR_PASSWORD",dialect:"org.hibernate.dialect.MySQLDialect"};case"postgresql":return{url:`jdbc:postgresql://localhost:5432/${a}`,username:"postgres",password:"YOUR_PASSWORD",dialect:"org.hibernate.dialect.PostgreSQLDialect"};default:return{url:`jdbc:h2:mem:${a}`,username:"sa",password:"",dialect:"org.hibernate.dialect.H2Dialect"}}}printSuccessMessage(e){let a=e.authStrategy==="jwt",r=e.projectType==="fullstack",i=e.buildTool==="maven"?"./mvnw spring-boot:run":"./gradlew bootRun";console.log(),l.success(`Project "${e.projectName}" is ready!
|
|
47
47
|
`),console.log(` Next steps:
|
|
48
|
-
`),console.log(` cd ${e.projectName}`),console.log(` ${i}`),console.log(),console.log(" Endpoints:"),console.log(" Home http://localhost:8080/"),r&&console.log(` Dashboard http://localhost:8080/${a?"":"dashboard"}`),e.modules.includes("Swagger")&&console.log(" API Docs http://localhost:8080/swagger-ui/index.html"),console.log(" Health http://localhost:8080/actuator/health"),console.log(),console.log(` Auth strategy: ${a?"JWT (stateless)":"Session (form-login)"}`),console.log(` Project type: ${r?"Fullstack":"REST API only"}`),console.log()}};function K(t){return!t||t.trim()===""?"Package name cannot be empty.":/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/.test(t.trim())?!0:"Package name must be lowercase, dot-separated identifiers (e.g. com.example.myapp)."}function X(t){return!t||t.trim()===""?"Project name cannot be empty.":/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(t.trim())?!0:"Project name must start with a letter and contain only alphanumerics, hyphens, or underscores."}function ee(t){return!t||t.trim()===""?"Entity name cannot be empty.":/^[A-Za-z][A-Za-z0-9]*$/.test(t.trim())?!0:"Entity name must start with a letter and contain only alphanumerics (PascalCase recommended)."}function te(t){return t.replace(/\./g,"/")}function ae(t){return t.charAt(0).toUpperCase()+t.slice(1)}x();async function re(t){
|
|
48
|
+
`),console.log(` cd ${e.projectName}`),console.log(` ${i}`),console.log(),console.log(" Endpoints:"),console.log(" Home http://localhost:8080/"),r&&console.log(` Dashboard http://localhost:8080/${a?"":"dashboard"}`),e.modules.includes("Swagger")&&console.log(" API Docs http://localhost:8080/swagger-ui/index.html"),console.log(" Health http://localhost:8080/actuator/health"),console.log(),console.log(` Auth strategy: ${a?"JWT (stateless)":"Session (form-login)"}`),console.log(` Project type: ${r?"Fullstack":"REST API only"}`),console.log()}};function K(t){return!t||t.trim()===""?"Package name cannot be empty.":/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/.test(t.trim())?!0:"Package name must be lowercase, dot-separated identifiers (e.g. com.example.myapp)."}function X(t){return!t||t.trim()===""?"Project name cannot be empty.":/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(t.trim())?!0:"Project name must start with a letter and contain only alphanumerics, hyphens, or underscores."}function ee(t){return!t||t.trim()===""?"Entity name cannot be empty.":/^[A-Za-z][A-Za-z0-9]*$/.test(t.trim())?!0:"Entity name must start with a letter and contain only alphanumerics (PascalCase recommended)."}function te(t){return t.replace(/\./g,"/")}function ae(t){return t.charAt(0).toUpperCase()+t.slice(1)}x();async function re(t){T("1.0.0");let e=X(t);typeof e=="string"&&(l.error(e),process.exit(1)),S("Configure Project");let a=await R.default.prompt([{type:"input",name:"packageName",message:"Package name:",default:`com.example.${t.toLowerCase().replace(/[^a-z0-9]/g,"")}`,validate:K},{type:"input",name:"description",message:"Project description:",default:`${t} Spring Boot Application`},{type:"list",name:"buildTool",message:"Build tool:",choices:[{name:"Maven (recommended)",value:"maven"},{name:"Gradle (Groovy DSL)",value:"gradle"}],default:"maven"},{type:"list",name:"database",message:"Database:",choices:[{name:"H2 (in-memory, great for dev/testing)",value:"h2"},{name:"MySQL ",value:"mysql"},{name:"PostgreSQL",value:"postgresql"}],default:"h2"},{type:"list",name:"javaVersion",message:"Java version:",choices:["21","17"],default:"21"}]);S("Authentication & Frontend");let r=await R.default.prompt([{type:"list",name:"authStrategy",message:"Authentication method:",choices:[{name:"JWT \u2014 stateless, token-based, ideal for REST + SPA (recommended)",value:"jwt"},{name:"Session \u2014 Spring form-login, stateful, ideal for Thymeleaf apps",value:"session"}],default:"jwt"},{type:"list",name:"projectType",message:"Project type:",choices:[{name:"REST API \u2014 JSON responses only, no frontend",value:"api"},{name:"Fullstack \u2014 REST API + frontend (dashboard, profiles, admin panel)",value:"fullstack"}],default:"api"}]);S("Select Modules");let i=await R.default.prompt([{type:"checkbox",name:"modules",message:"Optional modules (space to toggle):",choices:[{name:"Swagger / OpenAPI UI",value:"Swagger",checked:!0},{name:"Mail (Spring Mail SMTP)",value:"Mail"},{name:"Logging (Logback with file appender)",value:"Logging"}]}]),n={projectName:t,packageName:a.packageName,packagePath:te(a.packageName),database:a.database,buildTool:a.buildTool,modules:i.modules,javaVersion:a.javaVersion,springBootVersion:"3.2.4",description:a.description,authStrategy:r.authStrategy,projectType:r.projectType},o=new I;try{S(`Generating ${t}`),console.log(),await o.generate(n),l.done()}catch(c){console.log(),c instanceof Error?l.error(c.message):l.error("An unexpected error occurred."),process.exit(1)}}var U=j(require("inquirer")),J=j(require("fs-extra")),L=j(require("path"));var f=j(require("path")),z=j(require("fs-extra")),ie=j(require("ora"));C();x();var O=class{templatesDir;constructor(){this.templatesDir=f.default.resolve(__dirname,"../templates/entity")}async generate(e){l.title(`Generating entity: ${e.entityName}`);let a=(0,ie.default)({text:"Scaffolding entity files...",color:"cyan"}).start();try{let r=f.default.join(e.projectDir,"src/main/java",e.packagePath),i=f.default.join(e.projectDir,"src/test/java",e.packagePath);if(!await z.default.pathExists(r))throw new Error(`Cannot find Java source directory at "${r}". Make sure you are running this command inside a Sprygen-generated project.`);let n=this.buildContext(e);a.text="Writing entity, repository, service, controller...",await y(f.default.join(r,"entity",`${e.entityName}.java`),f.default.join(this.templatesDir,"Entity.java.ejs"),n),await y(f.default.join(r,"repository",`${e.entityName}Repository.java`),f.default.join(this.templatesDir,"EntityRepository.java.ejs"),n),await y(f.default.join(r,"service",`${e.entityName}Service.java`),f.default.join(this.templatesDir,"EntityService.java.ejs"),n),await y(f.default.join(r,"controller",`${e.entityName}Controller.java`),f.default.join(this.templatesDir,"EntityController.java.ejs"),n),await y(f.default.join(r,"dto",`${e.entityName}Dto.java`),f.default.join(this.templatesDir,"EntityDto.java.ejs"),n),a.text="Writing entity test...",await z.default.ensureDir(f.default.join(i,"controller")),await y(f.default.join(i,"controller",`${e.entityName}ControllerTest.java`),f.default.join(this.templatesDir,"EntityControllerTest.java.ejs"),n),a.succeed(`Entity "${e.entityName}" generated successfully!`)}catch(r){throw a.fail("Entity generation failed."),r}l.success(`
|
|
49
49
|
Entity "${e.entityName}" files created.
|
|
50
|
-
`)}buildContext(e){return{entityName:e.entityName,entityNameLower:e.entityNameLower,entityNameUpper:e.entityNameUpper,packageName:e.packageName,packagePath:e.packagePath,fields:e.fields,year:new Date().getFullYear()}}};x();async function oe(t){let e=ee(t);typeof e=="string"&&(
|
|
50
|
+
`)}buildContext(e){return{entityName:e.entityName,entityNameLower:e.entityNameLower,entityNameUpper:e.entityNameUpper,packageName:e.packageName,packagePath:e.packagePath,fields:e.fields,year:new Date().getFullYear()}}};x();async function oe(t){let e=ee(t);typeof e=="string"&&(l.error(e),process.exit(1));let a=process.cwd(),r="";try{let d=L.default.join(a,"src/main/java");if(await J.default.pathExists(d)){let h=await J.default.readdir(d);if(h.length>0){let b=L.default.join(d,h[0]);for(r=h[0];;){let k=(await J.default.readdir(b,{withFileTypes:!0})).filter(N=>N.isDirectory());if(k.length===1)r+="."+k[0].name,b=L.default.join(b,k[0].name);else break}}}}catch{}let i=await U.default.prompt([{type:"input",name:"packageName",message:"Base package name (e.g. com.example.myapp):",default:r||"com.example.myapp"}]),n=[],o=!0;for(l.info(`
|
|
51
51
|
Add fields to your entity (leave name empty to finish)
|
|
52
|
-
`);o;){let
|
|
52
|
+
`);o;){let d=await U.default.prompt([{type:"input",name:"name",message:"Field name (camelCase):"}]);if(!d.name||d.name.trim()===""){o=!1;break}let h=await U.default.prompt([{type:"list",name:"type",message:`Type for ${d.name}:`,choices:["String","Integer","Long","Boolean","Double","LocalDate","LocalDateTime"],default:"String"},{type:"confirm",name:"nullable",message:"Can it be null?",default:!1}]);n.push({name:d.name,type:h.type,nullable:h.nullable})}let p={entityName:ae(t),entityNameLower:t.toLowerCase(),entityNameUpper:t.toUpperCase(),packageName:i.packageName,packagePath:i.packageName.replace(/\./g,"/"),fields:n,projectDir:a},m=new O;try{await m.generate(p)}catch(d){d instanceof Error?l.error(d.message):l.error("An unexpected error occurred."),process.exit(1)}}var ce=j(require("inquirer")),W=j(require("fs-extra")),A=j(require("path"));var w=j(require("path")),ne=j(require("fs-extra")),se=j(require("ora"));C();x();var q=class{templatesDir;constructor(){this.templatesDir=w.default.resolve(__dirname,"../templates/auth")}async generate(e){l.title("Generating JWT Authentication");let a=(0,se.default)({text:"Scaffolding security files...",color:"cyan"}).start();try{let r=w.default.join(e.projectDir,"src/main/java",e.packagePath);if(!await ne.default.pathExists(r))throw new Error("Cannot find Java source directory. Make sure you are inside a Sprygen project directory.");let i={packageName:e.packageName,packagePath:e.packagePath,projectName:e.projectName,year:new Date().getFullYear()};a.text="Writing SecurityConfig.java...",await y(w.default.join(r,"config","SecurityConfig.java"),w.default.join(this.templatesDir,"SecurityConfig.java.ejs"),i),a.text="Writing JwtService.java...",await y(w.default.join(r,"service","JwtService.java"),w.default.join(this.templatesDir,"JwtService.java.ejs"),i),a.text="Writing JwtAuthFilter.java...",await y(w.default.join(r,"security","JwtAuthFilter.java"),w.default.join(this.templatesDir,"JwtAuthFilter.java.ejs"),i),a.text="Writing AuthController.java...",await y(w.default.join(r,"controller","AuthController.java"),w.default.join(this.templatesDir,"AuthController.java.ejs"),i),a.text="Writing UserDetailsServiceImpl.java...",await y(w.default.join(r,"security","UserDetailsServiceImpl.java"),w.default.join(this.templatesDir,"UserDetailsServiceImpl.java.ejs"),i),a.succeed("JWT authentication scaffolded successfully!")}catch(r){throw a.fail("Auth generation failed."),r}l.success(`
|
|
53
53
|
JWT auth files generated.
|
|
54
|
-
`),
|
|
55
|
-
`)}};x();async function le(){let t=process.cwd(),e="",a=A.default.basename(t);try{let o=A.default.join(t,"src/main/java");if(await
|
|
54
|
+
`),l.info(`Make sure your application.yml has: jwt.secret and jwt.expiration set.
|
|
55
|
+
`)}};x();async function le(){let t=process.cwd(),e="",a=A.default.basename(t);try{let o=A.default.join(t,"src/main/java");if(await W.default.pathExists(o)){let c=await W.default.readdir(o);if(c.length>0){let p=A.default.join(o,c[0]);for(e=c[0];;){let d=(await W.default.readdir(p,{withFileTypes:!0})).filter(h=>h.isDirectory());if(d.length===1)e+="."+d[0].name,p=A.default.join(p,d[0].name);else break}}}}catch{}let r=await ce.default.prompt([{type:"input",name:"packageName",message:"Base package name (e.g. com.example.myapp):",default:e||"com.example.myapp"}]),i={packageName:r.packageName,packagePath:r.packageName.replace(/\./g,"/"),projectDir:t,projectName:a},n=new q;try{await n.generate(i)}catch(o){o instanceof Error?l.error(o.message):l.error("An unexpected error occurred."),process.exit(1)}}var pe=require("child_process");x();function me(){T(),S("Updating Sprygen"),l.info("Fetching latest version from npm..."),(0,pe.exec)("npm install -g sprygen@latest",(t,e,a)=>{if(t){l.error("Failed to update Sprygen."),console.error(a);return}l.success("Successfully updated to the latest version!"),console.log()})}var D=new de.Command;D.name("sprygen").description("A production-ready Spring Boot project generator CLI").version("1.0.3");D.command("new <project-name>").description("Generate a new Spring Boot project").action(async t=>{await re(t)});D.command("add-entity <entity-name>").description("Generate a repository, service, controller, and test for a new entity in an existing project").action(async t=>{await oe(t)});D.command("generate-auth").description("Scaffold JWT authentication and security configuration in an existing project").action(async()=>{await le()});D.command("update").description("Update Sprygen to the latest version").action(()=>{me()});D.parse(process.argv);process.argv.slice(2).length||D.outputHelp();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sprygen",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "A production-ready Spring Boot project generator CLI. Scaffold secure, structured Java applications with built-in JWT or session authentication, role-based access control, user management, and an optional fullstack frontend — all from a single interactive command.",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|