Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "sql-helper",
"displayName": "SQL Helper",
"description": "Advanced extension with SQL, Java JDBC, Python and JavaScript/TypeScript snippets for databases",
"version": "0.5.2",
"version": "0.5.3",
"publisher": "marcosgdz03",
"engines": {
"vscode": "^1.106.0"
Expand Down Expand Up @@ -93,7 +93,7 @@
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile && npm run lint",
"lint": "eslint 'src/**/*.{ts,js}'",
"lint": "eslint src/**/*.ts",
"test": "node ./out/test/runTest.js"
},
"devDependencies": {
Expand Down
155 changes: 140 additions & 15 deletions src/generator/java/springGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,52 @@
import * as vscode from "vscode";
import * as fs from "fs";
import * as path from "path";

type Database = "PostgreSQL" | "MySQL" | "SQLite";

interface Template {
name: string;
description: string;
config: {
package: string;
architecture: string;
};
}

export async function chooseSpringTemplate(): Promise<Template | undefined> {
const templates: vscode.QuickPickItem[] = [
{
label: "📄 REST API Template",
description: "Simple REST controllers"
},
{
label: "🏛️ Clean Architecture",
description: "Ports & Adapters pattern"
},
{
label: "🔗 Microservices",
description: "Spring Cloud services"
},
];

const selected = await vscode.window.showQuickPick(templates, {
placeHolder: "Choose Spring Boot template"
});

if (!selected) {
return;
}

return {
name: selected.label,
description: selected.description || "",
config: {
package: "com.example.app",
architecture: selected.label.toLowerCase().replace(/\s+/g, "-"),
} as any
};
}

export async function generateSpringProject(
projectPath: string,
version: string,
Expand All @@ -16,15 +60,28 @@ export async function generateSpringProject(

fs.mkdirSync(basePackage, { recursive: true });
fs.mkdirSync(resources, { recursive: true });
["model", "dao", "controller"].forEach(folder => fs.mkdirSync(path.join(basePackage, folder), { recursive: true }));
["model", "dao", "controller"].forEach(folder =>
fs.mkdirSync(path.join(basePackage, folder), { recursive: true })
);

fs.writeFileSync(path.join(basePackage, "Application.java"), generateMainClass());
const application = generateMainClass();
fs.writeFileSync(path.join(basePackage, "Application.java"), application);

fs.writeFileSync(path.join(resources, "application.properties"), generateSpringProperties(db, dbDetails.dbName));
fs.writeFileSync(path.join(resources, "init.sql"), generateInitSql(dbDetails.dbName, dbDetails.tables, db));
const props = generateSpringProperties(db, dbDetails.dbName);
fs.writeFileSync(path.join(resources, "application.properties"), props);

if (buildTool === "maven") {fs.writeFileSync(path.join(projectPath, "pom.xml"), `<!-- POM for Spring Boot ${version} -->`);}
else {fs.writeFileSync(path.join(projectPath, "build.gradle"), `// Gradle for Spring Boot ${version}`);}
const sql = generateInitSql(dbDetails.dbName, dbDetails.tables, db);
fs.writeFileSync(path.join(resources, "init.sql"), sql);

if (buildTool === "maven") {
fs.writeFileSync(path.join(projectPath, "pom.xml"),
`<!-- POM for Spring Boot ${version} -->`
);
} else {
fs.writeFileSync(path.join(projectPath, "build.gradle"),
`// Gradle for Spring Boot ${version}`
);
}

console.log(`Spring Boot project generated at ${projectPath}`);
}
Expand All @@ -44,20 +101,88 @@ public class Application {
}

function generateSpringProperties(db: Database, dbName: string) {
let props = `server.port=8080\nspring.application.name=app\n`;
const props: string[] = ["server.port=8080", `spring.application.name=${dbName}`];

switch (db) {
case "PostgreSQL": props += `spring.datasource.url=jdbc:postgresql://localhost:5432/${dbName}\nspring.datasource.username=postgres\nspring.datasource.password=secret\n`; break;
case "MySQL": props += `spring.datasource.url=jdbc:mysql://localhost:3306/${dbName}\nspring.datasource.username=root\nspring.datasource.password=secret\n`; break;
case "SQLite": props += `spring.datasource.url=jdbc:sqlite:${dbName}.sqlite\n`; break;
case "PostgreSQL":
props.push("spring.datasource.url=jdbc:postgresql://localhost:5432/");
props.push(`${dbName}?schema=public`);
props.push("spring.datasource.username=postgres");
props.push("spring.datasource.password=secret");
break;
case "MySQL":
props.push("spring.datasource.url=jdbc:mysql://localhost:3306/");
props.push(`${dbName}?createDatabaseIfNotExist=true`);
props.push("spring.datasource.username=root");
props.push("spring.datasource.password=secret");
break;
case "SQLite":
props.push(`spring.datasource.url=jdbc:sqlite:${dbName}.sqlite`);
break;
}
return props;

return props.join("\\n");
}

function generateInitSql(dbName: string, tables: string[], db: Database) {
let sql = db !== "SQLite" ? `CREATE DATABASE IF NOT EXISTS ${dbName};\nUSE ${dbName};\n\n` : "";
const lines: string[] = [];

if (db !== "SQLite") {
lines.push(`CREATE DATABASE IF NOT EXISTS ${dbName};`);
lines.push(`USE ${dbName};`);
lines.push("");
}

tables.forEach(table => {
const idType = db === "PostgreSQL" ? "BIGSERIAL" : db === "MySQL" ? "BIGINT AUTO_INCREMENT" : "INTEGER PRIMARY KEY AUTOINCREMENT";
sql += `CREATE TABLE IF NOT EXISTS ${table} (\n id ${idType},\n name VARCHAR(255),\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\n`;
const idType = db === "PostgreSQL" ? "BIGSERIAL" :
db === "MySQL" ? "BIGINT AUTO_INCREMENT" :
"INTEGER PRIMARY KEY AUTOINCREMENT";

lines.push(`CREATE TABLE IF NOT EXISTS ${table} (
id ${idType},
name VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`);
lines.push("");
});
return sql;

return lines.join("\\n");
}

async function generateControllers(tables: string[]): Promise<void> {
const controllersDir = path.join(
process.cwd(),
"src", "generator", "java", "controllers"
);

if (!fs.existsSync(controllersDir)) {
fs.mkdirSync(controllersDir, { recursive: true });
}

const controllers = [
`UserController.java`,
`ProductController.java`
];

controllers.forEach(controller => {
const controllerPath = path.join(controllersDir, controller);
const controllerName = controller.replace(".java", "");

fs.writeFileSync(
controllerPath,
`package com.example.app.controller;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/${controllerName.toLowerCase()}")
public class ${controllerName}Controller {

@GetMapping
public List<String> getAll() {
return List.of();
}
}`
);
});
}
41 changes: 25 additions & 16 deletions src/snippets/generatorSnippets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,35 @@ import { chooseJsFramework } from "../generator/jsGenerator";

export async function showProjectGenerator() {
const languages: vscode.QuickPickItem[] = [
{ label: "Java", description: "Generate Java project boilerplate" },
{ label: "Python", description: "Generate Python project boilerplate" },
{ label: "JavaScript / TypeScript", description: "Generate JS/TS project boilerplate" }
{
label: "🔥 Java",
description: "Generate Spring Boot, Micronaut or Quarkus project"
},
{
label: "🐍 Python",
description: "Generate Flask or FastAPI project"
},
{
label: "⚡ JavaScript / TypeScript",
description: "Generate Express, NestJS or Next.js project"
}
];

const selectedLanguage = await vscode.window.showQuickPick(languages, {
placeHolder: "Choose a language for your project"
placeHolder: "Select a programming language to generate your project"
});

if (!selectedLanguage) {return;}

switch (selectedLanguage.label) {
case "Java":
await chooseJavaFramework();
break;
case "Python":
await choosePythonFramework();
break;
case "JavaScript / TypeScript":
await chooseJsFramework();
break;
if (!selectedLanguage) {
console.log("Project selection cancelled");
return;
}


const actions: Record<string, () => Promise<void>> = {
"🔥 Java": () => chooseJavaFramework(),
"🐍 Python": () => choosePythonFramework(),
"⚡ JavaScript / TypeScript": () => chooseJsFramework()
};

await actions[selectedLanguage.label]?.();
}
16 changes: 16 additions & 0 deletions src/templates/java/clean.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"templateName": "clean-architecture",
"description": "Clean Architecture with ports and adapters",
"version": "1.0.0",
"config": {
"package": "CleanArch",
"architecture": "clean-architecture",
"layers": ["domain", "application", "infrastructure", "presentation"],
"files": {
"domain": ["User", "UserService", "UserRepository"],
"application": ["UserApp", "UserCommand"],
"infrastructure": ["UserEntity", "MySQLEntity"],
"presentation": ["UserController", "UserRequest", "UserResponse"]
}
}
}
80 changes: 80 additions & 0 deletions src/templates/java/javaTemplates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as vscode from "vscode";
import * as fs from "fs";
import * as path from "path";

interface Template {
name: string;
description: string;
structure: {
controllers: string[];
models: string[];
services: string[];
};
dependencies: string[];
}

export async function chooseSpringTemplate(): Promise<Template> {
const templates: vscode.QuickPickItem[] = [
{ label: "📄 REST API Template", description: "Simple REST controllers" },
{ label: "🏛️ Clean Architecture", description: "Ports and adapters pattern" },
{ label: "🔗 Microservices", description: "Spring Cloud services" },
{ label: "⚙️ Project Structure", description: "Customizable structure" },
];

const result = await vscode.window.showQuickPick(templates, {
placeHolder: "Choose Spring Boot template",
});

if (!result) {
return {
name: "basic",
description: "Basic project structure",
structure: {
controllers: ["HelloController.java"],
models: ["User.java"],
services: ["UserService.java"],
},
dependencies: ["spring-boot-starter-web"],
} as Template;
}

// Parse templates from JSON files
const template = await loadTemplate(result.label);
return template;
}

async function loadTemplate(name: string): Promise<Template> {
const templatesDir = path.join(__dirname, "../../templates/java");
const templatesPath = await findTemplatesAsync(templatesDir);

const jsonPath = templatesPath.find((p) => path.basename(p) === `${name}.json`);

if (!jsonPath) {
throw new Error(`Template ${name} not found`);
}

const templateData = await fs.promises.readFile(jsonPath, "utf-8");
const template = JSON.parse(templateData);

return {
name: template.templateName,
description: template.description,
structure: template.config || {},
dependencies: template.dependencies || [],
} as Template;
}

async function findTemplatesAsync(dir: string): Promise<string[]> {
if (!fs.existsSync(dir)) {
return [];
}

try {
const files = fs.readdirSync(dir);
return files
.filter(file => file.endsWith(".json"))
.map(file => path.join(dir, file));
} catch (error) {
return [];
}
}
29 changes: 29 additions & 0 deletions src/templates/java/microservices.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"templateName": "microservices",
"description": "Microservices architecture with Spring Cloud",
"version": "1.0.0",
"config": {
"package": "Microservice",
"architecture": "microservices",
"services": {
"user-service": {
"package": "userService",
"controllers": ["UserController"],
"models": ["User"],
"services": ["UserService"],
"ports": ["UserPort"]
},
"product-service": {
"package": "productService",
"controllers": ["ProductController"],
"models": ["Product"],
"services": ["ProductService"],
"ports": ["ProductPort"]
}
},
"config": {
"discovery": "eureka",
"gateway": "spring-cloud-gateway"
}
}
}
Loading
Loading