archforge-x 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 +156 -56
- package/dist/cli/commands/sync.js +22 -0
- package/dist/cli/interactive.js +41 -3
- package/dist/core/architecture/schema.js +23 -2
- package/dist/core/architecture/validator.js +112 -0
- package/dist/generators/base.js +166 -0
- package/dist/generators/generator.js +35 -332
- package/dist/generators/go/gin.js +327 -0
- package/dist/generators/node/express.js +920 -0
- package/dist/generators/node/nestjs.js +770 -0
- package/dist/generators/node/nextjs.js +252 -0
- package/dist/generators/python/django.js +327 -0
- package/dist/generators/python/fastapi.js +309 -0
- package/dist/generators/registry.js +25 -0
- package/dist/index.js +29 -15
- package/package.json +3 -1
- package/dist/cli/init.js +0 -74
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BaseGenerator = void 0;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
11
|
+
class BaseGenerator {
|
|
12
|
+
async generate(arch, options) {
|
|
13
|
+
const projectName = options.projectName || arch.project.name || "generated-project";
|
|
14
|
+
const root = path_1.default.resolve(projectName);
|
|
15
|
+
console.log(chalk_1.default.blueBright(`š Starting ${this.getGeneratorName()} generation for ${projectName}...`));
|
|
16
|
+
if (!fs_extra_1.default.existsSync(root)) {
|
|
17
|
+
fs_extra_1.default.mkdirpSync(root);
|
|
18
|
+
}
|
|
19
|
+
await this.generateProjectStructure(root, arch, options);
|
|
20
|
+
this.generateArchForgeConfig(root, arch, options);
|
|
21
|
+
// Ensure all layer and custom folders exist
|
|
22
|
+
const config = js_yaml_1.default.load(fs_extra_1.default.readFileSync(path_1.default.join(root, "archforge.yaml"), "utf8"));
|
|
23
|
+
if (config.layers) {
|
|
24
|
+
for (const layer of Object.values(config.layers)) {
|
|
25
|
+
this.createDir(root, layer.path);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (config.customStructure?.enabled && config.customStructure.folders) {
|
|
29
|
+
for (const folder of config.customStructure.folders) {
|
|
30
|
+
this.createDir(root, folder.path);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (options.modules?.includes("docker")) {
|
|
34
|
+
console.log(chalk_1.default.blue(" š³ Generating Docker configuration..."));
|
|
35
|
+
this.generateDocker(root, options);
|
|
36
|
+
}
|
|
37
|
+
if (options.modules?.includes("ci")) {
|
|
38
|
+
console.log(chalk_1.default.blue(" š· Generating CI/CD workflows..."));
|
|
39
|
+
this.generateCI(root, options);
|
|
40
|
+
}
|
|
41
|
+
this.generateProductionDocs(root, arch, options);
|
|
42
|
+
this.ensureNoEmptyFolders(root);
|
|
43
|
+
console.log(chalk_1.default.green.bold(`\nā
${this.getGeneratorName()} project ${projectName} successfully generated!`));
|
|
44
|
+
}
|
|
45
|
+
generateProductionDocs(root, arch, options) {
|
|
46
|
+
this.writeFile(root, "docs/architecture.md", `
|
|
47
|
+
# Architecture Guide
|
|
48
|
+
|
|
49
|
+
This project follows the **${options.architecture || arch.metadata.type}** architecture style.
|
|
50
|
+
|
|
51
|
+
## Layers
|
|
52
|
+
${arch.layers.map(l => `- **${l.name}**: ${l.description || 'Located at ' + l.path}`).join('\n')}
|
|
53
|
+
|
|
54
|
+
## Rules
|
|
55
|
+
- **Layer Boundaries**: Enforced via \`archforge.yaml\`.
|
|
56
|
+
- **Naming**: Services must end with \`Service\`, Repositories with \`Repository\`.
|
|
57
|
+
`);
|
|
58
|
+
this.writeFile(root, "docs/database.md", `
|
|
59
|
+
# Database Guide
|
|
60
|
+
|
|
61
|
+
- **ORM**: ${options.orm || arch.metadata.orm}
|
|
62
|
+
- **Database**: ${options.database || arch.metadata.database}
|
|
63
|
+
|
|
64
|
+
## Setup
|
|
65
|
+
1. Ensure Docker is running.
|
|
66
|
+
2. Run \`docker-compose up -d\`.
|
|
67
|
+
3. Migrations are handled automatically on startup.
|
|
68
|
+
`);
|
|
69
|
+
this.writeFile(root, "docs/caching.md", `
|
|
70
|
+
# Caching Guide
|
|
71
|
+
|
|
72
|
+
This project implements a first-class caching layer.
|
|
73
|
+
|
|
74
|
+
## Strategies
|
|
75
|
+
- **Redis**: Used in production for distributed caching.
|
|
76
|
+
- **In-Memory**: Used for local development or as a fallback.
|
|
77
|
+
|
|
78
|
+
## Usage
|
|
79
|
+
Inject the \`ICache\` interface into your services.
|
|
80
|
+
`);
|
|
81
|
+
this.writeFile(root, "docs/rules.md", `
|
|
82
|
+
# ArchForge Rules
|
|
83
|
+
|
|
84
|
+
This project uses \`archforge.yaml\` to enforce architectural standards.
|
|
85
|
+
|
|
86
|
+
## How to Sync
|
|
87
|
+
Run \`npx archforge sync\` to validate the structure.
|
|
88
|
+
`);
|
|
89
|
+
}
|
|
90
|
+
ensureNoEmptyFolders(root) {
|
|
91
|
+
const scan = (dir) => {
|
|
92
|
+
const files = fs_extra_1.default.readdirSync(dir);
|
|
93
|
+
if (files.length === 0) {
|
|
94
|
+
this.writeFile(dir, "README.md", `# ${path_1.default.basename(dir)}\n\nThis folder is part of the architectural structure.`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
for (const file of files) {
|
|
98
|
+
const fullPath = path_1.default.join(dir, file);
|
|
99
|
+
if (fs_extra_1.default.statSync(fullPath).isDirectory()) {
|
|
100
|
+
scan(fullPath);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
scan(path_1.default.join(root, "src"));
|
|
106
|
+
}
|
|
107
|
+
generateArchForgeConfig(root, arch, options) {
|
|
108
|
+
const config = {
|
|
109
|
+
architecture: options.architecture || arch.metadata.type,
|
|
110
|
+
stack: options.framework || arch.metadata.framework,
|
|
111
|
+
orm: options.orm || arch.metadata.orm || "none",
|
|
112
|
+
database: options.database || arch.metadata.database || "none",
|
|
113
|
+
layers: arch.layers.length > 0 ? arch.layers.reduce((acc, layer) => {
|
|
114
|
+
acc[layer.name] = {
|
|
115
|
+
path: layer.path,
|
|
116
|
+
canImport: layer.canImport || layer.allowedImports || []
|
|
117
|
+
};
|
|
118
|
+
return acc;
|
|
119
|
+
}, {}) : this.getDefaultLayers(options.architecture || "clean"),
|
|
120
|
+
naming: arch.naming || {
|
|
121
|
+
services: "*Service",
|
|
122
|
+
repositories: "*Repository",
|
|
123
|
+
controllers: "*Controller"
|
|
124
|
+
},
|
|
125
|
+
rules: arch.rules || {
|
|
126
|
+
forbidDirectDbAccess: true,
|
|
127
|
+
forbidFrameworkInDomain: true,
|
|
128
|
+
enforceLayerBoundaries: true
|
|
129
|
+
},
|
|
130
|
+
customStructure: arch.customStructure || {
|
|
131
|
+
enabled: true,
|
|
132
|
+
folders: [
|
|
133
|
+
{ path: "src/modules" },
|
|
134
|
+
{ path: "src/shared" }
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
this.writeFile(root, "archforge.yaml", `
|
|
139
|
+
# ArchForge X Configuration
|
|
140
|
+
# This file is the single source of truth for your project architecture.
|
|
141
|
+
${require('js-yaml').dump(config)}
|
|
142
|
+
`.trim());
|
|
143
|
+
}
|
|
144
|
+
getDefaultLayers(arch) {
|
|
145
|
+
if (arch === "clean") {
|
|
146
|
+
return {
|
|
147
|
+
domain: { path: "src/domain", canImport: [] },
|
|
148
|
+
application: { path: "src/application", canImport: ["domain"] },
|
|
149
|
+
infrastructure: { path: "src/infrastructure", canImport: ["application", "domain"] },
|
|
150
|
+
presentation: { path: "src/presentation", canImport: ["application"] }
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// Add other defaults as needed
|
|
154
|
+
return {};
|
|
155
|
+
}
|
|
156
|
+
writeFile(root, filePath, content) {
|
|
157
|
+
const fullPath = path_1.default.join(root, filePath);
|
|
158
|
+
fs_extra_1.default.mkdirpSync(path_1.default.dirname(fullPath));
|
|
159
|
+
fs_extra_1.default.writeFileSync(fullPath, content.trim());
|
|
160
|
+
}
|
|
161
|
+
createDir(root, dirPath) {
|
|
162
|
+
const fullPath = path_1.default.join(root, dirPath);
|
|
163
|
+
fs_extra_1.default.mkdirpSync(fullPath);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
exports.BaseGenerator = BaseGenerator;
|
|
@@ -4,339 +4,42 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.generateProject = generateProject;
|
|
7
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Object.assign(deps, {
|
|
31
|
-
"@nestjs/common": "^10.0.0",
|
|
32
|
-
"@nestjs/core": "^10.0.0",
|
|
33
|
-
"@nestjs/platform-express": "^10.0.0",
|
|
34
|
-
"reflect-metadata": "^0.1.13",
|
|
35
|
-
"rxjs": "^7.8.0"
|
|
36
|
-
});
|
|
37
|
-
Object.assign(devDeps, {
|
|
38
|
-
"@nestjs/cli": "^10.0.0",
|
|
39
|
-
"@nestjs/schematics": "^10.0.0",
|
|
40
|
-
"@nestjs/testing": "^10.0.0"
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
else if (fw === "express") {
|
|
44
|
-
Object.assign(deps, { "express": "^4.18.2", "cors": "^2.8.5" });
|
|
45
|
-
Object.assign(devDeps, { "@types/express": "^4.17.17", "ts-node-dev": "^2.0.0" });
|
|
46
|
-
}
|
|
47
|
-
if (modules.includes("auth"))
|
|
48
|
-
deps["jsonwebtoken"] = "^9.0.0";
|
|
49
|
-
if (modules.includes("auth") && fw === "nestjs")
|
|
50
|
-
deps["@nestjs/jwt"] = "^10.0.0";
|
|
51
|
-
if (modules.includes("db"))
|
|
52
|
-
deps["typeorm"] = "^0.3.17";
|
|
53
|
-
if (modules.includes("cache"))
|
|
54
|
-
deps["ioredis"] = "^5.3.0";
|
|
55
|
-
return JSON.stringify({
|
|
56
|
-
name,
|
|
57
|
-
version: "0.0.1",
|
|
58
|
-
scripts: {
|
|
59
|
-
"start:dev": fw === "nestjs" ? "nest start --watch" : "ts-node-dev src/main.ts",
|
|
60
|
-
"build": "tsc",
|
|
61
|
-
"test": "jest"
|
|
62
|
-
},
|
|
63
|
-
dependencies: deps,
|
|
64
|
-
devDependencies: devDeps
|
|
65
|
-
}, null, 2);
|
|
66
|
-
};
|
|
67
|
-
const getGoMod = (name, fw) => {
|
|
68
|
-
return `module ${name}
|
|
69
|
-
|
|
70
|
-
go 1.21
|
|
71
|
-
|
|
72
|
-
require (
|
|
73
|
-
${fw === "gin" ? 'github.com/gin-gonic/gin v1.9.1' : ''}
|
|
74
|
-
github.com/joho/godotenv v1.5.1
|
|
75
|
-
)`;
|
|
76
|
-
};
|
|
77
|
-
const getRequirementsTxt = (fw, modules) => {
|
|
78
|
-
const reqs = ["python-dotenv"];
|
|
79
|
-
if (fw === "django")
|
|
80
|
-
reqs.push("Django>=4.2");
|
|
81
|
-
if (fw === "flask")
|
|
82
|
-
reqs.push("Flask>=2.3");
|
|
83
|
-
if (modules.includes("db"))
|
|
84
|
-
reqs.push("psycopg2-binary", "sqlalchemy");
|
|
85
|
-
if (modules.includes("auth"))
|
|
86
|
-
reqs.push("PyJWT");
|
|
87
|
-
return reqs.join("\n");
|
|
88
|
-
};
|
|
89
|
-
const getArchForgeYaml = (name, arch, lang, fw, modules) => {
|
|
90
|
-
let layers = "";
|
|
91
|
-
if (arch === "clean") {
|
|
92
|
-
layers = `
|
|
93
|
-
layers:
|
|
94
|
-
- name: "domain"
|
|
95
|
-
path: "src/domain"
|
|
96
|
-
description: "Enterprise business rules and entities"
|
|
97
|
-
forbiddenImports: ["application", "infrastructure", "interface-adapters"]
|
|
98
|
-
|
|
99
|
-
- name: "application"
|
|
100
|
-
path: "src/application"
|
|
101
|
-
description: "Application business rules and use cases"
|
|
102
|
-
allowedImports: ["domain"]
|
|
103
|
-
|
|
104
|
-
- name: "interface-adapters"
|
|
105
|
-
path: "src/interface-adapters"
|
|
106
|
-
description: "Controllers, presenters, and gateways"
|
|
107
|
-
allowedImports: ["domain", "application"]
|
|
108
|
-
|
|
109
|
-
- name: "infrastructure"
|
|
110
|
-
path: "src/infrastructure"
|
|
111
|
-
description: "Frameworks, drivers, and external tools"
|
|
112
|
-
allowedImports: ["domain", "application", "interface-adapters"]
|
|
113
|
-
`;
|
|
114
|
-
}
|
|
115
|
-
else if (arch === "ddd") {
|
|
116
|
-
layers = `
|
|
117
|
-
layers:
|
|
118
|
-
- name: "domain"
|
|
119
|
-
path: "src/domain"
|
|
120
|
-
description: "Core domain logic and aggregates"
|
|
121
|
-
forbiddenImports: ["application", "infrastructure", "interfaces"]
|
|
122
|
-
|
|
123
|
-
- name: "application"
|
|
124
|
-
path: "src/application"
|
|
125
|
-
description: "Application services and orchestration"
|
|
126
|
-
allowedImports: ["domain"]
|
|
127
|
-
|
|
128
|
-
- name: "infrastructure"
|
|
129
|
-
path: "src/infrastructure"
|
|
130
|
-
description: "Persistence and external service implementations"
|
|
131
|
-
allowedImports: ["domain", "application"]
|
|
132
|
-
|
|
133
|
-
- name: "interfaces"
|
|
134
|
-
path: "src/interfaces"
|
|
135
|
-
description: "Web APIs and user interfaces"
|
|
136
|
-
allowedImports: ["domain", "application"]
|
|
137
|
-
`;
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
// Default layered
|
|
141
|
-
layers = `
|
|
142
|
-
layers:
|
|
143
|
-
- name: "core"
|
|
144
|
-
path: "src/core"
|
|
145
|
-
description: "Core business logic"
|
|
146
|
-
|
|
147
|
-
- name: "services"
|
|
148
|
-
path: "src/services"
|
|
149
|
-
allowedImports: ["core"]
|
|
150
|
-
|
|
151
|
-
- name: "api"
|
|
152
|
-
path: "src/api"
|
|
153
|
-
allowedImports: ["core", "services"]
|
|
154
|
-
`;
|
|
155
|
-
}
|
|
156
|
-
return `
|
|
157
|
-
version: "1.0"
|
|
158
|
-
name: "${name} Architecture"
|
|
159
|
-
project:
|
|
160
|
-
name: "${name}"
|
|
161
|
-
root: "."
|
|
162
|
-
|
|
163
|
-
metadata:
|
|
164
|
-
type: "${arch}"
|
|
165
|
-
language: "${lang}"
|
|
166
|
-
framework: "${fw}"
|
|
167
|
-
modules: ${JSON.stringify(modules)}
|
|
168
|
-
|
|
169
|
-
${layers.trim()}
|
|
170
|
-
`.trim();
|
|
171
|
-
};
|
|
172
|
-
// --- CONTENT GENERATORS ---
|
|
173
|
-
const generateTsCleanArch = (root, options) => {
|
|
174
|
-
const fw = options.framework;
|
|
175
|
-
writeFile(root, "src/domain/entities/user.entity.ts", `
|
|
176
|
-
export class User {
|
|
177
|
-
constructor(
|
|
178
|
-
public readonly id: string,
|
|
179
|
-
public readonly name: string,
|
|
180
|
-
public readonly email: string,
|
|
181
|
-
public readonly passwordHash: string
|
|
182
|
-
) {}
|
|
183
|
-
}
|
|
184
|
-
`);
|
|
185
|
-
writeFile(root, "src/domain/repositories/user.repository.interface.ts", `
|
|
186
|
-
import { User } from "../entities/user.entity";
|
|
187
|
-
|
|
188
|
-
export interface IUserRepository {
|
|
189
|
-
save(user: User): Promise<User>;
|
|
190
|
-
findByEmail(email: string): Promise<User | null>;
|
|
191
|
-
findById(id: string): Promise<User | null>;
|
|
192
|
-
}
|
|
193
|
-
`);
|
|
194
|
-
writeFile(root, "src/application/use-cases/create-user.use-case.ts", `
|
|
195
|
-
import { User } from "../../domain/entities/user.entity";
|
|
196
|
-
import { IUserRepository } from "../../domain/repositories/user.repository.interface";
|
|
197
|
-
|
|
198
|
-
export class CreateUserUseCase {
|
|
199
|
-
constructor(private userRepository: IUserRepository) {}
|
|
200
|
-
|
|
201
|
-
async execute(name: string, email: string, passwordHash: string): Promise<User> {
|
|
202
|
-
const existing = await this.userRepository.findByEmail(email);
|
|
203
|
-
if (existing) throw new Error("User already exists");
|
|
204
|
-
|
|
205
|
-
const user = new User(Date.now().toString(), name, email, passwordHash);
|
|
206
|
-
return await this.userRepository.save(user);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
`);
|
|
210
|
-
if (fw === "express") {
|
|
211
|
-
writeFile(root, "src/interface-adapters/controllers/user.controller.ts", `
|
|
212
|
-
import { Request, Response } from "express";
|
|
213
|
-
import { CreateUserUseCase } from "../../application/use-cases/create-user.use-case";
|
|
214
|
-
|
|
215
|
-
export class UserController {
|
|
216
|
-
constructor(private createUserUseCase: CreateUserUseCase) {}
|
|
217
|
-
|
|
218
|
-
async create(req: Request, res: Response) {
|
|
219
|
-
try {
|
|
220
|
-
const { name, email, password } = req.body;
|
|
221
|
-
const user = await this.createUserUseCase.execute(name, email, password);
|
|
222
|
-
res.status(201).json(user);
|
|
223
|
-
} catch (e: any) {
|
|
224
|
-
res.status(400).json({ error: e.message });
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
`);
|
|
229
|
-
writeFile(root, "src/main.ts", `
|
|
230
|
-
import express from "express";
|
|
231
|
-
import { UserController } from "./interface-adapters/controllers/user.controller";
|
|
232
|
-
import { CreateUserUseCase } from "./application/use-cases/create-user.use-case";
|
|
233
|
-
import { InMemoryUserRepository } from "./infrastructure/repositories/in-memory-user.repository";
|
|
234
|
-
|
|
235
|
-
const app = express();
|
|
236
|
-
app.use(express.json());
|
|
237
|
-
|
|
238
|
-
const userRepo = new InMemoryUserRepository();
|
|
239
|
-
const createUserUseCase = new CreateUserUseCase(userRepo);
|
|
240
|
-
const userController = new UserController(createUserUseCase);
|
|
241
|
-
|
|
242
|
-
app.post("/users", (req, res) => userController.create(req, res));
|
|
243
|
-
|
|
244
|
-
app.listen(3000, () => console.log("š Server running on PORT 3000 (Clean Architecture)"));
|
|
245
|
-
`);
|
|
246
|
-
}
|
|
247
|
-
else if (fw === "nestjs") {
|
|
248
|
-
writeFile(root, "src/app.module.ts", `
|
|
249
|
-
import { Module } from '@nestjs/common';
|
|
250
|
-
import { UserController } from './interface-adapters/controllers/user.controller';
|
|
251
|
-
import { CreateUserUseCase } from './application/use-cases/create-user.use-case';
|
|
252
|
-
import { InMemoryUserRepository } from './infrastructure/repositories/in-memory-user.repository';
|
|
253
|
-
|
|
254
|
-
@Module({
|
|
255
|
-
controllers: [UserController],
|
|
256
|
-
providers: [
|
|
257
|
-
{ provide: 'IUserRepository', useClass: InMemoryUserRepository },
|
|
258
|
-
CreateUserUseCase
|
|
259
|
-
],
|
|
260
|
-
})
|
|
261
|
-
export class AppModule {}
|
|
262
|
-
`);
|
|
263
|
-
writeFile(root, "src/main.ts", `
|
|
264
|
-
import { NestFactory } from '@nestjs/core';
|
|
265
|
-
import { AppModule } from './app.module';
|
|
266
|
-
|
|
267
|
-
async function bootstrap() {
|
|
268
|
-
const app = await NestFactory.create(AppModule);
|
|
269
|
-
await app.listen(3000);
|
|
270
|
-
console.log("š NestJS Server with Clean Architecture running on 3000");
|
|
271
|
-
}
|
|
272
|
-
bootstrap();
|
|
273
|
-
`);
|
|
274
|
-
}
|
|
275
|
-
writeFile(root, "src/infrastructure/repositories/in-memory-user.repository.ts", `
|
|
276
|
-
import { User } from "../../domain/entities/user.entity";
|
|
277
|
-
import { IUserRepository } from "../../domain/repositories/user.repository.interface";
|
|
278
|
-
|
|
279
|
-
export class InMemoryUserRepository implements IUserRepository {
|
|
280
|
-
private users: User[] = [];
|
|
281
|
-
async save(user: User): Promise<User> {
|
|
282
|
-
this.users.push(user);
|
|
283
|
-
return user;
|
|
284
|
-
}
|
|
285
|
-
async findByEmail(email: string): Promise<User | null> {
|
|
286
|
-
return this.users.find(u => u.email === email) || null;
|
|
287
|
-
}
|
|
288
|
-
async findById(id: string): Promise<User | null> {
|
|
289
|
-
return this.users.find(u => u.id === id) || null;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
`);
|
|
293
|
-
};
|
|
294
|
-
// --- MAIN GENERATOR ---
|
|
8
|
+
const registry_1 = require("./registry");
|
|
9
|
+
// Import Generators
|
|
10
|
+
const express_1 = require("./node/express");
|
|
11
|
+
const nestjs_1 = require("./node/nestjs");
|
|
12
|
+
const nextjs_1 = require("./node/nextjs");
|
|
13
|
+
const fastapi_1 = require("./python/fastapi");
|
|
14
|
+
const django_1 = require("./python/django");
|
|
15
|
+
const gin_1 = require("./go/gin");
|
|
16
|
+
// Register Generators
|
|
17
|
+
registry_1.GeneratorRegistry.register("ts:express", new express_1.ExpressGenerator());
|
|
18
|
+
registry_1.GeneratorRegistry.register("js:express", new express_1.ExpressGenerator());
|
|
19
|
+
registry_1.GeneratorRegistry.register("ts:nestjs", new nestjs_1.NestJSGenerator());
|
|
20
|
+
registry_1.GeneratorRegistry.register("js:nestjs", new nestjs_1.NestJSGenerator()); // NestJS usually TS, but just in case
|
|
21
|
+
registry_1.GeneratorRegistry.register("ts:nextjs", new nextjs_1.NextJSGenerator()); // Next.js
|
|
22
|
+
registry_1.GeneratorRegistry.register("js:nextjs", new nextjs_1.NextJSGenerator());
|
|
23
|
+
registry_1.GeneratorRegistry.register("py:fastapi", new fastapi_1.FastAPIGenerator());
|
|
24
|
+
registry_1.GeneratorRegistry.register("py:django", new django_1.DjangoGenerator());
|
|
25
|
+
registry_1.GeneratorRegistry.register("py", new fastapi_1.FastAPIGenerator()); // Default Python
|
|
26
|
+
registry_1.GeneratorRegistry.register("go:gin", new gin_1.GoGinGenerator());
|
|
27
|
+
registry_1.GeneratorRegistry.register("go", new gin_1.GoGinGenerator()); // Default Go
|
|
295
28
|
async function generateProject(arch, options = {}) {
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
compilerOptions: { target: "ES2020", module: "commonjs", outDir: "./dist", rootDir: "./src", strict: true, experimentalDecorators: true, emitDecoratorMetadata: true },
|
|
312
|
-
exclude: ["node_modules", "dist"]
|
|
313
|
-
}, null, 2));
|
|
314
|
-
}
|
|
315
|
-
else if (lang === "go") {
|
|
316
|
-
writeFile(root, "go.mod", getGoMod(projectName, fw));
|
|
317
|
-
}
|
|
318
|
-
else if (lang === "py") {
|
|
319
|
-
writeFile(root, "requirements.txt", getRequirementsTxt(fw, modules));
|
|
320
|
-
}
|
|
321
|
-
writeFile(root, ".env.example", "PORT=3000\nDB_HOST=localhost\nJWT_SECRET=supersecret");
|
|
322
|
-
writeFile(root, ".gitignore", "node_modules/\ndist/\n.env\n__pycache__/\n*.exe\nvendor/");
|
|
323
|
-
writeFile(root, "README.md", `# ${projectName}\nGenerated by ArchForge X\nArchitecture: ${archStyle}`);
|
|
324
|
-
// Generate archforge.yaml Rules File
|
|
325
|
-
writeFile(root, "archforge.yaml", getArchForgeYaml(projectName, archStyle, lang, fw, modules));
|
|
326
|
-
if (lang === "ts" || lang === "js") {
|
|
327
|
-
generateTsCleanArch(root, options);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
// Standard Mode: Just layers and READMEs
|
|
332
|
-
for (const layer of arch.layers) {
|
|
333
|
-
const layerPath = path_1.default.resolve(root, layer.path);
|
|
334
|
-
fs_extra_1.default.mkdirpSync(layerPath);
|
|
335
|
-
const readmeContent = `# ${layer.name} Layer\n\nPath: ${layer.path}\n\nAllowed Imports: ${layer.allowedImports?.join(", ") || "Any"}\nForbidden Imports: ${layer.forbiddenImports?.join(", ") || "None"}\n`;
|
|
336
|
-
fs_extra_1.default.writeFileSync(path_1.default.join(layerPath, "README.md"), readmeContent);
|
|
337
|
-
const baseFileContent = `/**\n * Base file for ${layer.name} layer\n * Generated by ArchForge X\n */\n`;
|
|
338
|
-
fs_extra_1.default.writeFileSync(path_1.default.join(layerPath, `${layer.name}.ts`), baseFileContent);
|
|
339
|
-
}
|
|
29
|
+
const lang = options.language || arch.metadata.language || "ts";
|
|
30
|
+
const fw = options.framework || arch.metadata.framework || "";
|
|
31
|
+
console.log(chalk_1.default.blueBright(`š Looking for generator for ${lang} + ${fw}...`));
|
|
32
|
+
const generator = registry_1.GeneratorRegistry.getGenerator(lang, fw);
|
|
33
|
+
if (!generator) {
|
|
34
|
+
console.error(chalk_1.default.red(`ā No generator found for language: ${lang} and framework: ${fw}`));
|
|
35
|
+
console.log(chalk_1.default.yellow(`Available generators: ${registry_1.GeneratorRegistry.listGenerators().join(", ")}`));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await generator.generate(arch, options);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error(chalk_1.default.red("ā Error during generation:"), error);
|
|
43
|
+
throw error;
|
|
340
44
|
}
|
|
341
|
-
console.log(chalk_1.default.green.bold(`\nā
Project ${projectName} successfully generated!`));
|
|
342
45
|
}
|