archforge-x 1.0.0 ā 1.0.3
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/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 +10 -1
- package/package.json +6 -4
- package/dist/generators/professional.js +0 -509
- package/dist/generators/templates/base.js +0 -10
- package/dist/sample-app/application/application.js +0 -8
- package/dist/sample-app/domain/domain.js +0 -2
- package/dist/sample-app/domain/domain.spec.js +0 -2
- package/dist/sample-app/infrastructure/infrastructure.js +0 -8
|
@@ -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
|
}
|