@currentjs/gen 0.1.1
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/CHANGELOG.md +7 -0
- package/LICENSE +56 -0
- package/README.md +686 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +143 -0
- package/dist/commands/commit.d.ts +1 -0
- package/dist/commands/commit.js +153 -0
- package/dist/commands/createApp.d.ts +1 -0
- package/dist/commands/createApp.js +64 -0
- package/dist/commands/createModule.d.ts +1 -0
- package/dist/commands/createModule.js +121 -0
- package/dist/commands/diff.d.ts +1 -0
- package/dist/commands/diff.js +164 -0
- package/dist/commands/generateAll.d.ts +4 -0
- package/dist/commands/generateAll.js +305 -0
- package/dist/commands/infer.d.ts +1 -0
- package/dist/commands/infer.js +179 -0
- package/dist/generators/controllerGenerator.d.ts +20 -0
- package/dist/generators/controllerGenerator.js +280 -0
- package/dist/generators/domainModelGenerator.d.ts +33 -0
- package/dist/generators/domainModelGenerator.js +175 -0
- package/dist/generators/serviceGenerator.d.ts +39 -0
- package/dist/generators/serviceGenerator.js +379 -0
- package/dist/generators/storeGenerator.d.ts +31 -0
- package/dist/generators/storeGenerator.js +191 -0
- package/dist/generators/templateGenerator.d.ts +11 -0
- package/dist/generators/templateGenerator.js +143 -0
- package/dist/generators/templates/appTemplates.d.ts +27 -0
- package/dist/generators/templates/appTemplates.js +1621 -0
- package/dist/generators/templates/controllerTemplates.d.ts +43 -0
- package/dist/generators/templates/controllerTemplates.js +82 -0
- package/dist/generators/templates/index.d.ts +5 -0
- package/dist/generators/templates/index.js +21 -0
- package/dist/generators/templates/serviceTemplates.d.ts +15 -0
- package/dist/generators/templates/serviceTemplates.js +54 -0
- package/dist/generators/templates/storeTemplates.d.ts +9 -0
- package/dist/generators/templates/storeTemplates.js +260 -0
- package/dist/generators/templates/validationTemplates.d.ts +25 -0
- package/dist/generators/templates/validationTemplates.js +66 -0
- package/dist/generators/templates/viewTemplates.d.ts +16 -0
- package/dist/generators/templates/viewTemplates.js +359 -0
- package/dist/generators/validationGenerator.d.ts +24 -0
- package/dist/generators/validationGenerator.js +199 -0
- package/dist/utils/cliUtils.d.ts +6 -0
- package/dist/utils/cliUtils.js +71 -0
- package/dist/utils/colors.d.ts +26 -0
- package/dist/utils/colors.js +80 -0
- package/dist/utils/commitUtils.d.ts +46 -0
- package/dist/utils/commitUtils.js +377 -0
- package/dist/utils/constants.d.ts +52 -0
- package/dist/utils/constants.js +64 -0
- package/dist/utils/generationRegistry.d.ts +25 -0
- package/dist/utils/generationRegistry.js +192 -0
- package/howto.md +556 -0
- package/package.json +44 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.handleGenerateAll = handleGenerateAll;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const cliUtils_1 = require("../utils/cliUtils");
|
|
40
|
+
const yaml_1 = require("yaml");
|
|
41
|
+
const domainModelGenerator_1 = require("../generators/domainModelGenerator");
|
|
42
|
+
const validationGenerator_1 = require("../generators/validationGenerator");
|
|
43
|
+
const serviceGenerator_1 = require("../generators/serviceGenerator");
|
|
44
|
+
const controllerGenerator_1 = require("../generators/controllerGenerator");
|
|
45
|
+
const storeGenerator_1 = require("../generators/storeGenerator");
|
|
46
|
+
const templateGenerator_1 = require("../generators/templateGenerator");
|
|
47
|
+
const generationRegistry_1 = require("../utils/generationRegistry");
|
|
48
|
+
const colors_1 = require("../utils/colors");
|
|
49
|
+
const constants_1 = require("../utils/constants");
|
|
50
|
+
async function handleGenerateAll(yamlPathArg, _outArg, moduleName, opts) {
|
|
51
|
+
var _a, _b;
|
|
52
|
+
const appYamlPath = (0, cliUtils_1.resolveYamlPath)(yamlPathArg);
|
|
53
|
+
(0, generationRegistry_1.initGenerationRegistry)(process.cwd());
|
|
54
|
+
const raw = fs.readFileSync(appYamlPath, 'utf8');
|
|
55
|
+
const appConfig = (0, yaml_1.parse)(raw);
|
|
56
|
+
const modulesList = ((_a = appConfig === null || appConfig === void 0 ? void 0 : appConfig.modules) !== null && _a !== void 0 ? _a : []).map(m => (typeof m === 'string' ? m : m.module));
|
|
57
|
+
const providersConfig = appConfig === null || appConfig === void 0 ? void 0 : appConfig.providers;
|
|
58
|
+
const databaseProviderName = appConfig === null || appConfig === void 0 ? void 0 : appConfig.database;
|
|
59
|
+
const shouldIncludeModule = (moduleYamlRel) => {
|
|
60
|
+
if (!moduleName || moduleName === '*')
|
|
61
|
+
return true;
|
|
62
|
+
const moduleNameLc = moduleName.toLowerCase();
|
|
63
|
+
const relNormalized = moduleYamlRel.replace(/\\/g, '/').toLowerCase();
|
|
64
|
+
if (relNormalized.endsWith(`/${moduleNameLc}.yaml`))
|
|
65
|
+
return true;
|
|
66
|
+
const moduleYamlPath = path.isAbsolute(moduleYamlRel)
|
|
67
|
+
? moduleYamlRel
|
|
68
|
+
: path.resolve(process.cwd(), moduleYamlRel);
|
|
69
|
+
const dirName = path.basename(path.dirname(moduleYamlPath)).toLowerCase();
|
|
70
|
+
if (dirName === moduleNameLc)
|
|
71
|
+
return true;
|
|
72
|
+
// Allow passing a path fragment
|
|
73
|
+
if (relNormalized.includes(`/${moduleNameLc}/`) || relNormalized.endsWith(`/${moduleNameLc}`))
|
|
74
|
+
return true;
|
|
75
|
+
return false;
|
|
76
|
+
};
|
|
77
|
+
const filteredModules = modulesList.filter(shouldIncludeModule);
|
|
78
|
+
if (filteredModules.length === 0) {
|
|
79
|
+
// eslint-disable-next-line no-console
|
|
80
|
+
console.warn(colors_1.colors.yellow(`No modules matched: ${moduleName}`));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const domainGen = new domainModelGenerator_1.DomainModelGenerator();
|
|
84
|
+
const valGen = new validationGenerator_1.ValidationGenerator();
|
|
85
|
+
const svcGen = new serviceGenerator_1.ServiceGenerator();
|
|
86
|
+
const ctrlGen = new controllerGenerator_1.ControllerGenerator();
|
|
87
|
+
const storeGen = new storeGenerator_1.StoreGenerator();
|
|
88
|
+
const tplGen = new templateGenerator_1.TemplateGenerator();
|
|
89
|
+
const initsBySrcDir = new Map();
|
|
90
|
+
// Run modules sequentially to avoid overlapping interactive prompts
|
|
91
|
+
for (const moduleYamlRel of filteredModules) {
|
|
92
|
+
const moduleYamlPath = path.isAbsolute(moduleYamlRel)
|
|
93
|
+
? moduleYamlRel
|
|
94
|
+
: path.resolve(process.cwd(), moduleYamlRel);
|
|
95
|
+
if (!fs.existsSync(moduleYamlPath)) {
|
|
96
|
+
// eslint-disable-next-line no-console
|
|
97
|
+
console.warn(colors_1.colors.yellow(`Module YAML not found: ${moduleYamlPath}`));
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const moduleDir = path.dirname(moduleYamlPath);
|
|
101
|
+
// Output folders inside module structure
|
|
102
|
+
const domainOut = path.join(moduleDir, 'domain', 'entities');
|
|
103
|
+
const appOut = path.join(moduleDir, 'application');
|
|
104
|
+
const infraOut = path.join(moduleDir, 'infrastructure');
|
|
105
|
+
fs.mkdirSync(domainOut, { recursive: true });
|
|
106
|
+
fs.mkdirSync(appOut, { recursive: true });
|
|
107
|
+
fs.mkdirSync(infraOut, { recursive: true });
|
|
108
|
+
// Domain entities
|
|
109
|
+
// eslint-disable-next-line no-await-in-loop
|
|
110
|
+
await domainGen.generateAndSaveFiles(moduleYamlPath, domainOut, { force: !!(opts === null || opts === void 0 ? void 0 : opts.force), skipOnConflict: !!(opts === null || opts === void 0 ? void 0 : opts.skip) });
|
|
111
|
+
// Generate and save via per-generator write logic
|
|
112
|
+
// eslint-disable-next-line no-await-in-loop
|
|
113
|
+
await valGen.generateAndSaveFiles(moduleYamlPath, appOut, { force: !!(opts === null || opts === void 0 ? void 0 : opts.force), skipOnConflict: !!(opts === null || opts === void 0 ? void 0 : opts.skip) });
|
|
114
|
+
await svcGen.generateAndSaveFiles(moduleYamlPath, appOut, { force: !!(opts === null || opts === void 0 ? void 0 : opts.force), skipOnConflict: !!(opts === null || opts === void 0 ? void 0 : opts.skip) });
|
|
115
|
+
const generatedControllers = await ctrlGen.generateAndSaveFiles(moduleYamlPath, infraOut, { force: !!(opts === null || opts === void 0 ? void 0 : opts.force), skipOnConflict: !!(opts === null || opts === void 0 ? void 0 : opts.skip) });
|
|
116
|
+
await tplGen.generateAndSaveFiles(moduleYamlPath, undefined, { force: !!(opts === null || opts === void 0 ? void 0 : opts.force), skipOnConflict: !!(opts === null || opts === void 0 ? void 0 : opts.skip) });
|
|
117
|
+
// Find nearest ancestor containing src/app.ts and collect controller inits for a single write later
|
|
118
|
+
let probeDir = moduleDir;
|
|
119
|
+
let srcDir = null;
|
|
120
|
+
for (let i = 0; i < 6; i += 1) {
|
|
121
|
+
const candidate = path.join(probeDir, 'src', constants_1.COMMON_FILES.APP_TS);
|
|
122
|
+
if (fs.existsSync(candidate)) {
|
|
123
|
+
srcDir = path.join(probeDir, 'src');
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
const parent = path.dirname(probeDir);
|
|
127
|
+
if (parent === probeDir)
|
|
128
|
+
break;
|
|
129
|
+
probeDir = parent;
|
|
130
|
+
}
|
|
131
|
+
if (!srcDir)
|
|
132
|
+
continue;
|
|
133
|
+
const list = (_b = initsBySrcDir.get(srcDir)) !== null && _b !== void 0 ? _b : [];
|
|
134
|
+
for (const filePath of generatedControllers) {
|
|
135
|
+
const rel = path
|
|
136
|
+
.relative(srcDir, filePath)
|
|
137
|
+
.replace(/\\/g, '/')
|
|
138
|
+
.replace(/\.ts$/, '');
|
|
139
|
+
const ctrlName = path.basename(rel);
|
|
140
|
+
const importPath = rel.startsWith('.') ? rel : `./${rel}`;
|
|
141
|
+
const baseEntityName = ctrlName.endsWith('ApiController')
|
|
142
|
+
? ctrlName.slice(0, -'ApiController'.length)
|
|
143
|
+
: ctrlName.endsWith('WebController')
|
|
144
|
+
? ctrlName.slice(0, -'WebController'.length)
|
|
145
|
+
: ctrlName.replace('Controller', '');
|
|
146
|
+
const entityName = baseEntityName;
|
|
147
|
+
const entityVar = entityName.charAt(0).toLowerCase() + entityName.slice(1);
|
|
148
|
+
const m = rel.match(/modules\/([^/]+)\/infrastructure\/controllers\//);
|
|
149
|
+
const moduleFolder = m ? m[1] : undefined;
|
|
150
|
+
if (!moduleFolder)
|
|
151
|
+
continue;
|
|
152
|
+
const storeImportPath = `${constants_1.PATH_PATTERNS.MODULES_RELATIVE}${moduleFolder}/${constants_1.PATH_PATTERNS.INFRASTRUCTURE}/${constants_1.PATH_PATTERNS.STORES}/${entityName}${constants_1.GENERATOR_SUFFIXES.STORE}`;
|
|
153
|
+
const serviceImportPath = `${constants_1.PATH_PATTERNS.MODULES_RELATIVE}${moduleFolder}/${constants_1.PATH_PATTERNS.APPLICATION}/${constants_1.PATH_PATTERNS.SERVICES}/${entityName}${constants_1.GENERATOR_SUFFIXES.SERVICE}`;
|
|
154
|
+
//const authImportPath = `./modules/${moduleFolder}/application/auth/AuthService`;
|
|
155
|
+
// const hasAuth = true;//fs.existsSync(path.join(srcDir, authImportPath + '.ts'));
|
|
156
|
+
const init = {
|
|
157
|
+
ctrlName,
|
|
158
|
+
entityName,
|
|
159
|
+
entityVar,
|
|
160
|
+
importController: `import { ${ctrlName} } from '${importPath}';`,
|
|
161
|
+
importStore: `import { ${entityName}Store } from '${storeImportPath}';`,
|
|
162
|
+
importService: `import { ${entityName}Service } from '${serviceImportPath}';`,
|
|
163
|
+
importAuth: undefined, //hasAuth ? `import { AuthService } from '${authImportPath}';` : undefined,
|
|
164
|
+
wiring: [
|
|
165
|
+
`const ${entityVar}Store = new ${entityName}Store(db);`,
|
|
166
|
+
`const ${entityVar}Service = new ${entityName}Service(${entityVar}Store);`
|
|
167
|
+
],
|
|
168
|
+
registration: `new ${ctrlName}(${entityVar}Service)`
|
|
169
|
+
};
|
|
170
|
+
// Avoid duplicates by controller (keep both Api/Web), dedupe wiring later by entityName
|
|
171
|
+
if (!list.find((x) => x.ctrlName === init.ctrlName))
|
|
172
|
+
list.push(init);
|
|
173
|
+
}
|
|
174
|
+
initsBySrcDir.set(srcDir, list);
|
|
175
|
+
await storeGen.generateAndSaveFiles(moduleYamlPath, infraOut, { force: !!(opts === null || opts === void 0 ? void 0 : opts.force), skipOnConflict: !!(opts === null || opts === void 0 ? void 0 : opts.skip) });
|
|
176
|
+
}
|
|
177
|
+
// Single write per app: inject imports and rewrite block between markers
|
|
178
|
+
let isIProviderImported = false;
|
|
179
|
+
for (const [srcDir, controllerInits] of initsBySrcDir.entries()) {
|
|
180
|
+
try {
|
|
181
|
+
const appTsPath = path.join(srcDir, constants_1.COMMON_FILES.APP_TS);
|
|
182
|
+
if (!fs.existsSync(appTsPath))
|
|
183
|
+
continue;
|
|
184
|
+
let appTs = fs.readFileSync(appTsPath, 'utf8');
|
|
185
|
+
// Build providers import and initialization from app.yaml providers section
|
|
186
|
+
const importLines = [];
|
|
187
|
+
const providerInitLines = [];
|
|
188
|
+
const providersArrayEntries = [];
|
|
189
|
+
if (providersConfig && Object.keys(providersConfig).length > 0) {
|
|
190
|
+
for (const [provName, mod] of Object.entries(providersConfig)) {
|
|
191
|
+
if (!mod)
|
|
192
|
+
continue;
|
|
193
|
+
// Assume default import name from module spec after last '/'
|
|
194
|
+
const baseName = mod.split('/').pop() || 'provider';
|
|
195
|
+
const className = baseName
|
|
196
|
+
.replace(/^[^a-zA-Z_]*/g, '')
|
|
197
|
+
.split(/[-_]/)
|
|
198
|
+
.map(s => s.charAt(0).toUpperCase() + s.slice(1))
|
|
199
|
+
.join('');
|
|
200
|
+
importLines.push(`import { ${className}${!isIProviderImported ? ', IProvider, ISqlProvider' : ''} } from '${mod}';`);
|
|
201
|
+
if (!isIProviderImported)
|
|
202
|
+
isIProviderImported = true;
|
|
203
|
+
// Read provider configuration from env by name, parse JSON if possible, pass as is
|
|
204
|
+
providerInitLines.push(` ${provName}: new ${className}((() => {
|
|
205
|
+
const raw = process.env.${provName.toUpperCase()} || '';
|
|
206
|
+
try { return raw ? JSON.parse(raw) : undefined; } catch { return raw; }
|
|
207
|
+
})())`);
|
|
208
|
+
providersArrayEntries.push(provName);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
// Fallback to MySQL provider if no providers configured
|
|
213
|
+
importLines.push(`import { ProviderMysql, ISqlProvider } from '@currentjs/provider-mysql';`);
|
|
214
|
+
providerInitLines.push(` mysql: new ProviderMysql((() => {
|
|
215
|
+
const raw = process.env.MYSQL || '';
|
|
216
|
+
try { return raw ? JSON.parse(raw) : undefined; } catch { return raw; }
|
|
217
|
+
})())`);
|
|
218
|
+
providersArrayEntries.push('mysql');
|
|
219
|
+
}
|
|
220
|
+
const ensureDbLine = `const providers: Record<string, IProvider> = {\n${providerInitLines.join(',\n')}\n};\nconst db = providers['${databaseProviderName || providersArrayEntries[0]}'] as ISqlProvider;`;
|
|
221
|
+
// Ensure router import for server (app template already imports templating)
|
|
222
|
+
if (!appTs.includes("from '@currentjs/router'")) {
|
|
223
|
+
importLines.push(`import { createWebServer, createStaticServer } from '@currentjs/router';`);
|
|
224
|
+
}
|
|
225
|
+
for (const il of importLines) {
|
|
226
|
+
if (!appTs.includes(il)) {
|
|
227
|
+
appTs = il + '\n' + appTs;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Ensure MySQL provider import exists
|
|
231
|
+
if (!appTs.includes("from '@currentjs/provider-mysql'")) {
|
|
232
|
+
importLines.push(`import { ProviderMysql } from '@currentjs/provider-mysql';`);
|
|
233
|
+
}
|
|
234
|
+
for (const init of controllerInits) {
|
|
235
|
+
const maybe = [init.importController, init.importStore, init.importService];
|
|
236
|
+
if (init.importAuth)
|
|
237
|
+
maybe.push(init.importAuth);
|
|
238
|
+
for (const line of maybe) {
|
|
239
|
+
if (!appTs.includes(line) && !importLines.includes(line))
|
|
240
|
+
importLines.push(line);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (importLines.length) {
|
|
244
|
+
const existingImports = new Set();
|
|
245
|
+
const importRegex = /^import[^;]+;$/gm;
|
|
246
|
+
const currentImports = appTs.match(importRegex) || [];
|
|
247
|
+
currentImports.forEach(line => existingImports.add(line.trim()));
|
|
248
|
+
const toAdd = importLines.filter(line => !existingImports.has(line.trim()));
|
|
249
|
+
if (toAdd.length)
|
|
250
|
+
appTs = toAdd.join('\n') + '\n' + appTs;
|
|
251
|
+
}
|
|
252
|
+
// Compose fresh block content
|
|
253
|
+
const wiringLines = [];
|
|
254
|
+
// DB placeholder once
|
|
255
|
+
wiringLines.push(ensureDbLine);
|
|
256
|
+
const wiredEntities = new Set();
|
|
257
|
+
for (const init of controllerInits) {
|
|
258
|
+
if (!wiredEntities.has(init.entityName)) {
|
|
259
|
+
wiringLines.push(...init.wiring);
|
|
260
|
+
wiredEntities.add(init.entityName);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const registrations = controllerInits.map((i) => i.registration);
|
|
264
|
+
wiringLines.push('const controllers = [');
|
|
265
|
+
wiringLines.push(` ${registrations.join(',\n ')}`);
|
|
266
|
+
wiringLines.push('];');
|
|
267
|
+
const startMarker = constants_1.GENERATOR_MARKERS.CONTROLLERS_START;
|
|
268
|
+
const endMarker = constants_1.GENERATOR_MARKERS.CONTROLLERS_END;
|
|
269
|
+
const startIdx = appTs.indexOf(startMarker);
|
|
270
|
+
const endIdx = appTs.indexOf(endMarker);
|
|
271
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
272
|
+
const before = appTs.slice(0, startIdx + startMarker.length);
|
|
273
|
+
const after = appTs.slice(endIdx);
|
|
274
|
+
const block = '\n' + wiringLines.join('\n') + '\n';
|
|
275
|
+
appTs = before + block + after;
|
|
276
|
+
}
|
|
277
|
+
// Ensure the createWebServer call includes renderer in options
|
|
278
|
+
// Case 1: createWebServer(controllers, { ... })
|
|
279
|
+
const withOptionsRegex = /createWebServer\(\s*controllers\s*,\s*\{([\s\S]*?)\}\s*\)/m;
|
|
280
|
+
if (withOptionsRegex.test(appTs)) {
|
|
281
|
+
appTs = appTs.replace(withOptionsRegex, (full, inner) => {
|
|
282
|
+
if (/\brenderer\b\s*:/.test(inner))
|
|
283
|
+
return full; // already present
|
|
284
|
+
const trimmed = inner.trim();
|
|
285
|
+
const prefix = trimmed.length ? inner.replace(trimmed, '') : '';
|
|
286
|
+
const suffix = inner.endsWith(trimmed) ? '' : inner.slice(inner.indexOf(trimmed) + trimmed.length);
|
|
287
|
+
const sep = trimmed.length ? ', ' : '';
|
|
288
|
+
return full.replace(inner, `${prefix}${trimmed}${sep}renderer` + `: renderer${suffix}`);
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
// Case 2: createWebServer(controllers)
|
|
293
|
+
const noOptionsRegex = /createWebServer\(\s*controllers\s*\)/m;
|
|
294
|
+
if (noOptionsRegex.test(appTs)) {
|
|
295
|
+
appTs = appTs.replace(noOptionsRegex, 'createWebServer(controllers, { renderer })');
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
fs.writeFileSync(appTsPath, appTs, 'utf8');
|
|
299
|
+
}
|
|
300
|
+
catch (e) {
|
|
301
|
+
// eslint-disable-next-line no-console
|
|
302
|
+
console.warn(colors_1.colors.yellow(`Could not update app.ts with controllers: ${e instanceof Error ? e.message : String(e)}`));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function handleInfer(entityFileArg?: string, write?: boolean): void;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.handleInfer = handleInfer;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const yaml_1 = require("yaml");
|
|
40
|
+
const cliUtils_1 = require("../utils/cliUtils");
|
|
41
|
+
function reverseType(tsType) {
|
|
42
|
+
const t = tsType.trim();
|
|
43
|
+
if (t === 'string')
|
|
44
|
+
return 'string';
|
|
45
|
+
if (t === 'number')
|
|
46
|
+
return 'number';
|
|
47
|
+
if (t === 'boolean')
|
|
48
|
+
return 'boolean';
|
|
49
|
+
if (t === 'Date')
|
|
50
|
+
return 'datetime';
|
|
51
|
+
if (t === 'any[]')
|
|
52
|
+
return 'array';
|
|
53
|
+
if (t === 'object')
|
|
54
|
+
return 'object';
|
|
55
|
+
if (t === 'any')
|
|
56
|
+
return 'json';
|
|
57
|
+
return 'json';
|
|
58
|
+
}
|
|
59
|
+
function inferFromConstructorParams(paramsSrc) {
|
|
60
|
+
// Remove newlines to simplify parsing
|
|
61
|
+
const src = paramsSrc.replace(/\n/g, ' ');
|
|
62
|
+
const fields = [];
|
|
63
|
+
const regex = /public\s+(\w+)(\?)?\s*:\s*([^=,]+)(\s*=\s*([^,]+))?/g;
|
|
64
|
+
let match;
|
|
65
|
+
let index = 0;
|
|
66
|
+
while ((match = regex.exec(src))) {
|
|
67
|
+
const name = match[1];
|
|
68
|
+
const optionalMark = !!match[2];
|
|
69
|
+
const tsType = match[3].trim();
|
|
70
|
+
const hasDefault = !!match[4];
|
|
71
|
+
if (index === 0 && name === 'id') {
|
|
72
|
+
// Skip id
|
|
73
|
+
index += 1;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const yamlType = reverseType(tsType);
|
|
77
|
+
const field = { name, type: yamlType };
|
|
78
|
+
if (hasDefault)
|
|
79
|
+
field.auto = true;
|
|
80
|
+
// required: if optional mark present then not required; if auto true, mark not required as well
|
|
81
|
+
if (optionalMark || hasDefault)
|
|
82
|
+
field.required = false;
|
|
83
|
+
else
|
|
84
|
+
field.required = true;
|
|
85
|
+
fields.push(field);
|
|
86
|
+
index += 1;
|
|
87
|
+
}
|
|
88
|
+
return fields;
|
|
89
|
+
}
|
|
90
|
+
function parseEntity(filePath) {
|
|
91
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
92
|
+
const classMatch = content.match(/export\s+class\s+(\w+)\s*\{/);
|
|
93
|
+
if (!classMatch) {
|
|
94
|
+
throw new Error(`Cannot find exported class in ${filePath}`);
|
|
95
|
+
}
|
|
96
|
+
const entityName = classMatch[1];
|
|
97
|
+
// Extract constructor params
|
|
98
|
+
const ctorMatch = content.match(/constructor\s*\(([^)]*)\)/s);
|
|
99
|
+
if (!ctorMatch) {
|
|
100
|
+
throw new Error(`Cannot find constructor in ${filePath}`);
|
|
101
|
+
}
|
|
102
|
+
const paramsSrc = ctorMatch[1];
|
|
103
|
+
const fields = inferFromConstructorParams(paramsSrc);
|
|
104
|
+
return { entityName, fields };
|
|
105
|
+
}
|
|
106
|
+
function findModuleYamlForEntity(entityFile) {
|
|
107
|
+
const abs = (0, cliUtils_1.toAbsolute)(entityFile);
|
|
108
|
+
// Expect .../src/modules/<ModuleName>/domain/entities/<Entity>.ts
|
|
109
|
+
const parts = abs.split(path.sep);
|
|
110
|
+
const idx = parts.lastIndexOf('modules');
|
|
111
|
+
if (idx === -1 || idx + 1 >= parts.length)
|
|
112
|
+
return null;
|
|
113
|
+
const moduleName = parts[idx + 1];
|
|
114
|
+
// Build absolute module dir (handle leading path segment on POSIX correctly)
|
|
115
|
+
const moduleDir = path.isAbsolute(abs)
|
|
116
|
+
? path.join(path.sep, ...parts.slice(1, idx + 2))
|
|
117
|
+
: path.join(...parts.slice(0, idx + 2));
|
|
118
|
+
// Try to find any yaml in module dir (prefer one matching module name)
|
|
119
|
+
try {
|
|
120
|
+
const entries = fs.readdirSync(moduleDir);
|
|
121
|
+
const yamls = entries.filter(e => e.toLowerCase().endsWith('.yaml'));
|
|
122
|
+
if (yamls.length > 0) {
|
|
123
|
+
const preferred = yamls.find(y => y.toLowerCase() === `${moduleName.toLowerCase()}.yaml`);
|
|
124
|
+
const chosen = preferred !== null && preferred !== void 0 ? preferred : yamls[0];
|
|
125
|
+
return path.join(moduleDir, chosen);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// ignore
|
|
130
|
+
}
|
|
131
|
+
// Fallback to conventional name
|
|
132
|
+
const moduleYaml = path.join(moduleDir, `${moduleName.toLowerCase()}.yaml`);
|
|
133
|
+
return moduleYaml;
|
|
134
|
+
}
|
|
135
|
+
function handleInfer(entityFileArg, write) {
|
|
136
|
+
if (!entityFileArg) {
|
|
137
|
+
throw new Error('Usage: currentjs infer --file <path/to/Entity.ts> [--write]');
|
|
138
|
+
}
|
|
139
|
+
const entityFile = (0, cliUtils_1.toAbsolute)(entityFileArg);
|
|
140
|
+
if (!fs.existsSync(entityFile)) {
|
|
141
|
+
throw new Error(`Entity file not found: ${entityFile}`);
|
|
142
|
+
}
|
|
143
|
+
const { entityName, fields } = parseEntity(entityFile);
|
|
144
|
+
const modelDesc = {
|
|
145
|
+
name: entityName,
|
|
146
|
+
fields
|
|
147
|
+
};
|
|
148
|
+
if (!write) {
|
|
149
|
+
// Print a YAML snippet for the model only
|
|
150
|
+
// eslint-disable-next-line no-console
|
|
151
|
+
console.log((0, yaml_1.stringify)({ models: [modelDesc] }));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const moduleYamlPath = findModuleYamlForEntity(entityFile);
|
|
155
|
+
if (!moduleYamlPath) {
|
|
156
|
+
throw new Error('Could not determine module YAML path from entity file location. Use --write only within a module structure.');
|
|
157
|
+
}
|
|
158
|
+
let config = {};
|
|
159
|
+
if (fs.existsSync(moduleYamlPath)) {
|
|
160
|
+
try {
|
|
161
|
+
config = (0, yaml_1.parse)(fs.readFileSync(moduleYamlPath, 'utf8')) || {};
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
config = {};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!config.models)
|
|
168
|
+
config.models = [];
|
|
169
|
+
const idx = config.models.findIndex((m) => m && m.name === entityName);
|
|
170
|
+
if (idx === -1)
|
|
171
|
+
config.models.push(modelDesc);
|
|
172
|
+
else
|
|
173
|
+
config.models[idx] = modelDesc;
|
|
174
|
+
fs.mkdirSync(path.dirname(moduleYamlPath), { recursive: true });
|
|
175
|
+
const yamlOut = (0, yaml_1.stringify)(config);
|
|
176
|
+
fs.writeFileSync(moduleYamlPath, yamlOut, 'utf8');
|
|
177
|
+
// eslint-disable-next-line no-console
|
|
178
|
+
console.log(`Updated ${path.relative(process.cwd(), path.resolve(moduleYamlPath))} with model ${entityName}`);
|
|
179
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare class ControllerGenerator {
|
|
2
|
+
private hasPermissions;
|
|
3
|
+
private getActionPermissions;
|
|
4
|
+
private shouldGenerateMethod;
|
|
5
|
+
private needsUserParam;
|
|
6
|
+
private getHttpMethodName;
|
|
7
|
+
private getHttpDecorator;
|
|
8
|
+
private generateMethodImplementation;
|
|
9
|
+
private replaceTemplateVars;
|
|
10
|
+
private generateControllerMethod;
|
|
11
|
+
private getWebMethodName;
|
|
12
|
+
private getPathSuffix;
|
|
13
|
+
private getReturnType;
|
|
14
|
+
private generateController;
|
|
15
|
+
generateFromYamlFile(yamlFilePath: string): Record<string, string>;
|
|
16
|
+
generateAndSaveFiles(yamlFilePath?: string, outputDir?: string, opts?: {
|
|
17
|
+
force?: boolean;
|
|
18
|
+
skipOnConflict?: boolean;
|
|
19
|
+
}): Promise<string[]>;
|
|
20
|
+
}
|