@currentjs/gen 0.3.2 → 0.5.0
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 +10 -611
- package/README.md +623 -427
- package/dist/cli.js +2 -1
- package/dist/commands/commit.js +25 -42
- package/dist/commands/createApp.js +1 -0
- package/dist/commands/createModule.js +151 -45
- package/dist/commands/diff.js +27 -40
- package/dist/commands/generateAll.js +141 -291
- package/dist/commands/migrateCommit.js +6 -18
- package/dist/commands/migratePush.d.ts +1 -0
- package/dist/commands/migratePush.js +135 -0
- package/dist/commands/migrateUpdate.d.ts +1 -0
- package/dist/commands/migrateUpdate.js +147 -0
- package/dist/commands/newGenerateAll.d.ts +4 -0
- package/dist/commands/newGenerateAll.js +336 -0
- package/dist/generators/controllerGenerator.d.ts +43 -19
- package/dist/generators/controllerGenerator.js +547 -329
- package/dist/generators/domainLayerGenerator.d.ts +21 -0
- package/dist/generators/domainLayerGenerator.js +276 -0
- package/dist/generators/dtoGenerator.d.ts +21 -0
- package/dist/generators/dtoGenerator.js +518 -0
- package/dist/generators/newControllerGenerator.d.ts +55 -0
- package/dist/generators/newControllerGenerator.js +644 -0
- package/dist/generators/newServiceGenerator.d.ts +19 -0
- package/dist/generators/newServiceGenerator.js +266 -0
- package/dist/generators/newStoreGenerator.d.ts +39 -0
- package/dist/generators/newStoreGenerator.js +408 -0
- package/dist/generators/newTemplateGenerator.d.ts +29 -0
- package/dist/generators/newTemplateGenerator.js +510 -0
- package/dist/generators/serviceGenerator.d.ts +16 -51
- package/dist/generators/serviceGenerator.js +167 -586
- package/dist/generators/storeGenerator.d.ts +35 -32
- package/dist/generators/storeGenerator.js +291 -238
- package/dist/generators/storeGeneratorV2.d.ts +31 -0
- package/dist/generators/storeGeneratorV2.js +190 -0
- package/dist/generators/templateGenerator.d.ts +21 -21
- package/dist/generators/templateGenerator.js +393 -268
- package/dist/generators/templates/appTemplates.d.ts +3 -1
- package/dist/generators/templates/appTemplates.js +15 -10
- package/dist/generators/templates/data/appYamlTemplate +5 -2
- package/dist/generators/templates/data/cursorRulesTemplate +315 -221
- package/dist/generators/templates/data/frontendScriptTemplate +45 -11
- package/dist/generators/templates/data/mainViewTemplate +1 -1
- package/dist/generators/templates/data/systemTsTemplate +5 -0
- package/dist/generators/templates/index.d.ts +0 -3
- package/dist/generators/templates/index.js +0 -3
- package/dist/generators/templates/newStoreTemplates.d.ts +5 -0
- package/dist/generators/templates/newStoreTemplates.js +141 -0
- package/dist/generators/templates/storeTemplates.d.ts +1 -5
- package/dist/generators/templates/storeTemplates.js +102 -219
- package/dist/generators/templates/viewTemplates.js +1 -1
- package/dist/generators/useCaseGenerator.d.ts +13 -0
- package/dist/generators/useCaseGenerator.js +188 -0
- package/dist/types/configTypes.d.ts +148 -0
- package/dist/types/configTypes.js +10 -0
- package/dist/utils/childEntityUtils.d.ts +18 -0
- package/dist/utils/childEntityUtils.js +78 -0
- package/dist/utils/commandUtils.d.ts +43 -0
- package/dist/utils/commandUtils.js +124 -0
- package/dist/utils/commitUtils.d.ts +4 -1
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/constants.js +13 -1
- package/dist/utils/diResolver.d.ts +32 -0
- package/dist/utils/diResolver.js +204 -0
- package/dist/utils/new_parts_of_migrationUtils.d.ts +0 -0
- package/dist/utils/new_parts_of_migrationUtils.js +164 -0
- package/dist/utils/typeUtils.d.ts +19 -0
- package/dist/utils/typeUtils.js +70 -0
- package/package.json +7 -3
|
@@ -38,89 +38,64 @@ const fs = __importStar(require("fs"));
|
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const cliUtils_1 = require("../utils/cliUtils");
|
|
40
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
41
|
const generationRegistry_1 = require("../utils/generationRegistry");
|
|
48
42
|
const colors_1 = require("../utils/colors");
|
|
49
43
|
const constants_1 = require("../utils/constants");
|
|
44
|
+
const configTypes_1 = require("../types/configTypes");
|
|
45
|
+
const commandUtils_1 = require("../utils/commandUtils");
|
|
46
|
+
const diResolver_1 = require("../utils/diResolver");
|
|
47
|
+
const appTemplates_1 = require("../generators/templates/appTemplates");
|
|
50
48
|
async function handleGenerateAll(yamlPathArg, _outArg, moduleName, opts) {
|
|
51
|
-
var _a, _b;
|
|
49
|
+
var _a, _b, _c, _d;
|
|
52
50
|
const appYamlPath = (0, cliUtils_1.resolveYamlPath)(yamlPathArg);
|
|
53
51
|
(0, generationRegistry_1.initGenerationRegistry)(process.cwd());
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
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) {
|
|
52
|
+
const appConfig = (0, commandUtils_1.loadAppConfig)(appYamlPath);
|
|
53
|
+
const moduleEntries = (0, commandUtils_1.getModuleEntries)(appConfig);
|
|
54
|
+
const providersConfig = appConfig.providers;
|
|
55
|
+
const defaultDatabaseKey = (_a = appConfig.config) === null || _a === void 0 ? void 0 : _a.database;
|
|
56
|
+
const filteredEntries = moduleEntries.filter(entry => (0, commandUtils_1.shouldIncludeModule)(entry.path, moduleName) || (moduleName && entry.name === moduleName));
|
|
57
|
+
if (filteredEntries.length === 0) {
|
|
79
58
|
// eslint-disable-next-line no-console
|
|
80
59
|
console.warn(colors_1.colors.yellow(`No modules matched: ${moduleName}`));
|
|
81
60
|
return;
|
|
82
61
|
}
|
|
83
|
-
const domainGen =
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const moduleConfigByFolder = new Map(); // Cache module configs by folder name
|
|
91
|
-
// Run modules sequentially to avoid overlapping interactive prompts
|
|
92
|
-
for (const moduleYamlRel of filteredModules) {
|
|
93
|
-
const moduleYamlPath = path.isAbsolute(moduleYamlRel)
|
|
94
|
-
? moduleYamlRel
|
|
95
|
-
: path.resolve(process.cwd(), moduleYamlRel);
|
|
62
|
+
const { domainGen, dtoGen, useCaseGen, serviceGen, controllerGen, templateGen, storeGen } = (0, commandUtils_1.createGenerators)();
|
|
63
|
+
const moduleScansBySrcDir = new Map();
|
|
64
|
+
// Process each module: generate files and collect module dirs for DI scanning
|
|
65
|
+
for (const entry of filteredEntries) {
|
|
66
|
+
const moduleYamlPath = path.isAbsolute(entry.path)
|
|
67
|
+
? entry.path
|
|
68
|
+
: path.resolve(process.cwd(), entry.path);
|
|
96
69
|
if (!fs.existsSync(moduleYamlPath)) {
|
|
97
70
|
// eslint-disable-next-line no-console
|
|
98
71
|
console.warn(colors_1.colors.yellow(`Module YAML not found: ${moduleYamlPath}`));
|
|
99
72
|
continue;
|
|
100
73
|
}
|
|
101
|
-
const moduleDir = path.dirname(moduleYamlPath);
|
|
102
|
-
const moduleFolderName = path.basename(moduleDir);
|
|
103
|
-
// Parse and cache module config for dependency detection
|
|
104
74
|
const moduleYamlContent = fs.readFileSync(moduleYamlPath, 'utf8');
|
|
105
75
|
const moduleConfig = (0, yaml_1.parse)(moduleYamlContent);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
//
|
|
76
|
+
if (!(0, configTypes_1.isValidModuleConfig)(moduleConfig)) {
|
|
77
|
+
// eslint-disable-next-line no-console
|
|
78
|
+
console.warn(colors_1.colors.yellow(`Skipping ${moduleYamlPath}: not in expected format (missing domain/useCases)`));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const moduleDir = path.dirname(moduleYamlPath);
|
|
82
|
+
// eslint-disable-next-line no-console
|
|
83
|
+
console.log(colors_1.colors.blue(`\nGenerating module: ${path.basename(moduleDir)}`));
|
|
84
|
+
// eslint-disable-next-line no-await-in-loop
|
|
85
|
+
await domainGen.generateAndSaveFiles(moduleYamlPath, moduleDir, opts);
|
|
86
|
+
// eslint-disable-next-line no-await-in-loop
|
|
87
|
+
await dtoGen.generateAndSaveFiles(moduleYamlPath, moduleDir, opts);
|
|
88
|
+
// eslint-disable-next-line no-await-in-loop
|
|
89
|
+
await useCaseGen.generateAndSaveFiles(moduleYamlPath, moduleDir, opts);
|
|
90
|
+
// eslint-disable-next-line no-await-in-loop
|
|
91
|
+
await serviceGen.generateAndSaveFiles(moduleYamlPath, moduleDir, opts);
|
|
92
|
+
// eslint-disable-next-line no-await-in-loop
|
|
93
|
+
await storeGen.generateAndSaveFiles(moduleYamlPath, moduleDir, opts);
|
|
115
94
|
// eslint-disable-next-line no-await-in-loop
|
|
116
|
-
await
|
|
117
|
-
// Generate and save via per-generator write logic
|
|
95
|
+
await controllerGen.generateAndSaveFiles(moduleYamlPath, moduleDir, opts);
|
|
118
96
|
// eslint-disable-next-line no-await-in-loop
|
|
119
|
-
await
|
|
120
|
-
|
|
121
|
-
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) });
|
|
122
|
-
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) });
|
|
123
|
-
// Find nearest ancestor containing src/app.ts and collect controller inits for a single write later
|
|
97
|
+
await templateGen.generateAndSaveFiles(moduleYamlPath, moduleDir, opts);
|
|
98
|
+
// Find srcDir by probing upward for app.ts
|
|
124
99
|
let probeDir = moduleDir;
|
|
125
100
|
let srcDir = null;
|
|
126
101
|
for (let i = 0; i < 6; i += 1) {
|
|
@@ -136,149 +111,46 @@ async function handleGenerateAll(yamlPathArg, _outArg, moduleName, opts) {
|
|
|
136
111
|
}
|
|
137
112
|
if (!srcDir)
|
|
138
113
|
continue;
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (moduleConfig && moduleConfig.models) {
|
|
143
|
-
const m = moduleYamlPath.match(/modules\/([^/]+)\//);
|
|
144
|
-
const moduleFolder = m ? m[1] : path.basename(moduleDir);
|
|
145
|
-
for (const model of moduleConfig.models) {
|
|
146
|
-
const entityName = model.name;
|
|
147
|
-
const entityVar = entityName.charAt(0).toLowerCase() + entityName.slice(1);
|
|
148
|
-
// Check if we already have an entry for this entity
|
|
149
|
-
if (list.find(x => x.entityName === entityName)) {
|
|
150
|
-
continue; // Skip if already added via controller
|
|
151
|
-
}
|
|
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
|
-
// Detect dependencies
|
|
155
|
-
const storeDeps = [];
|
|
156
|
-
const serviceDeps = [];
|
|
157
|
-
if (model.fields) {
|
|
158
|
-
model.fields.forEach((field) => {
|
|
159
|
-
const isRelationship = moduleConfig.models.some((m) => m.name === field.type);
|
|
160
|
-
if (isRelationship) {
|
|
161
|
-
storeDeps.push(field.type);
|
|
162
|
-
serviceDeps.push(field.type);
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
// Add a minimal init entry (no controller, just for dependency resolution)
|
|
167
|
-
const init = {
|
|
168
|
-
ctrlName: '', // No controller
|
|
169
|
-
entityName,
|
|
170
|
-
entityVar,
|
|
171
|
-
importController: '',
|
|
172
|
-
importStore: `import { ${entityName}Store } from '${storeImportPath}';`,
|
|
173
|
-
importService: `import { ${entityName}Service } from '${serviceImportPath}';`,
|
|
174
|
-
importAuth: undefined,
|
|
175
|
-
wiring: [],
|
|
176
|
-
registration: '',
|
|
177
|
-
storeDependencies: storeDeps,
|
|
178
|
-
serviceDependencies: serviceDeps
|
|
179
|
-
};
|
|
180
|
-
list.push(init);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
// Then process controllers
|
|
184
|
-
for (const filePath of generatedControllers) {
|
|
185
|
-
const rel = path
|
|
186
|
-
.relative(srcDir, filePath)
|
|
187
|
-
.replace(/\\/g, '/')
|
|
188
|
-
.replace(/\.ts$/, '');
|
|
189
|
-
const ctrlName = path.basename(rel);
|
|
190
|
-
const importPath = rel.startsWith('.') ? rel : `./${rel}`;
|
|
191
|
-
const baseEntityName = ctrlName.endsWith('ApiController')
|
|
192
|
-
? ctrlName.slice(0, -'ApiController'.length)
|
|
193
|
-
: ctrlName.endsWith('WebController')
|
|
194
|
-
? ctrlName.slice(0, -'WebController'.length)
|
|
195
|
-
: ctrlName.replace('Controller', '');
|
|
196
|
-
const entityName = baseEntityName;
|
|
197
|
-
const entityVar = entityName.charAt(0).toLowerCase() + entityName.slice(1);
|
|
198
|
-
const m = rel.match(/modules\/([^/]+)\/infrastructure\/controllers\//);
|
|
199
|
-
const moduleFolder = m ? m[1] : undefined;
|
|
200
|
-
if (!moduleFolder)
|
|
201
|
-
continue;
|
|
202
|
-
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}`;
|
|
203
|
-
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}`;
|
|
204
|
-
// Look up the correct module config for this controller
|
|
205
|
-
const controllerModuleConfig = moduleConfigByFolder.get(moduleFolder);
|
|
206
|
-
// Detect store and service dependencies from module config
|
|
207
|
-
const storeDeps = [];
|
|
208
|
-
const serviceDeps = [];
|
|
209
|
-
if (controllerModuleConfig && controllerModuleConfig.models) {
|
|
210
|
-
const model = controllerModuleConfig.models.find((m) => m.name === entityName);
|
|
211
|
-
if (model && model.fields) {
|
|
212
|
-
model.fields.forEach((field) => {
|
|
213
|
-
// Check if this field is a relationship (type is another model name)
|
|
214
|
-
const isRelationship = controllerModuleConfig.models.some((m) => m.name === field.type);
|
|
215
|
-
if (isRelationship) {
|
|
216
|
-
storeDeps.push(field.type);
|
|
217
|
-
serviceDeps.push(field.type);
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
// Check if we already have a base entry for this entity (from all models)
|
|
223
|
-
const existingEntry = list.find(x => x.entityName === entityName && !x.ctrlName);
|
|
224
|
-
if (existingEntry) {
|
|
225
|
-
// Update existing entry to add controller info
|
|
226
|
-
existingEntry.ctrlName = ctrlName;
|
|
227
|
-
existingEntry.importController = `import { ${ctrlName} } from '${importPath}';`;
|
|
228
|
-
existingEntry.registration = `new ${ctrlName}(${entityVar}Service)`;
|
|
229
|
-
// Keep the dependencies we already detected
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
// Create new entry with controller
|
|
233
|
-
const init = {
|
|
234
|
-
ctrlName,
|
|
235
|
-
entityName,
|
|
236
|
-
entityVar,
|
|
237
|
-
importController: `import { ${ctrlName} } from '${importPath}';`,
|
|
238
|
-
importStore: `import { ${entityName}Store } from '${storeImportPath}';`,
|
|
239
|
-
importService: `import { ${entityName}Service } from '${serviceImportPath}';`,
|
|
240
|
-
importAuth: undefined,
|
|
241
|
-
wiring: [],
|
|
242
|
-
registration: `new ${ctrlName}(${entityVar}Service)`,
|
|
243
|
-
storeDependencies: storeDeps,
|
|
244
|
-
serviceDependencies: serviceDeps
|
|
245
|
-
};
|
|
246
|
-
if (!list.find((x) => x.ctrlName === init.ctrlName)) {
|
|
247
|
-
list.push(init);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
initsBySrcDir.set(srcDir, list);
|
|
252
|
-
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) });
|
|
114
|
+
const scans = (_b = moduleScansBySrcDir.get(srcDir)) !== null && _b !== void 0 ? _b : [];
|
|
115
|
+
scans.push({ moduleDir, databaseKey: entry.database });
|
|
116
|
+
moduleScansBySrcDir.set(srcDir, scans);
|
|
253
117
|
}
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
for (const [srcDir, controllerInits] of initsBySrcDir.entries()) {
|
|
118
|
+
// Update app.ts files with DI-based wiring
|
|
119
|
+
for (const [srcDir, moduleScans] of moduleScansBySrcDir.entries()) {
|
|
257
120
|
try {
|
|
258
121
|
const appTsPath = path.join(srcDir, constants_1.COMMON_FILES.APP_TS);
|
|
259
122
|
if (!fs.existsSync(appTsPath))
|
|
260
123
|
continue;
|
|
124
|
+
// Ensure system.ts exists (for existing apps created before this feature)
|
|
125
|
+
const systemTsPath = path.join(srcDir, 'system.ts');
|
|
126
|
+
if (!fs.existsSync(systemTsPath)) {
|
|
127
|
+
fs.writeFileSync(systemTsPath, appTemplates_1.systemTsTemplate, 'utf8');
|
|
128
|
+
// eslint-disable-next-line no-console
|
|
129
|
+
console.log(colors_1.colors.green(`Created ${systemTsPath}`));
|
|
130
|
+
}
|
|
261
131
|
let appTs = fs.readFileSync(appTsPath, 'utf8');
|
|
262
|
-
//
|
|
132
|
+
// --- Scan all modules for @Injectable and @Controller classes ---
|
|
133
|
+
const allClasses = [];
|
|
134
|
+
for (const scan of moduleScans) {
|
|
135
|
+
const classes = (0, diResolver_1.scanModuleClasses)(scan.moduleDir);
|
|
136
|
+
allClasses.push(...classes);
|
|
137
|
+
}
|
|
138
|
+
// --- Build provider info ---
|
|
263
139
|
const importLines = [];
|
|
264
140
|
const providerInitLines = [];
|
|
265
141
|
const providersArrayEntries = [];
|
|
142
|
+
let isIProviderImported = false;
|
|
266
143
|
if (providersConfig && Object.keys(providersConfig).length > 0) {
|
|
267
144
|
for (const [provName, mod] of Object.entries(providersConfig)) {
|
|
268
145
|
if (!mod)
|
|
269
146
|
continue;
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
.split(/[-_]/)
|
|
275
|
-
.map(s => s.charAt(0).toUpperCase() + s.slice(1))
|
|
276
|
-
.join('');
|
|
277
|
-
importLines.push(`import { ${className}${!isIProviderImported ? ', IProvider, ISqlProvider' : ''} } from '${mod}';`);
|
|
147
|
+
const resolved = (0, diResolver_1.resolveProviderImport)(mod, srcDir);
|
|
148
|
+
if (!appTs.includes(`from '${resolved.importPath}'`)) {
|
|
149
|
+
importLines.push(`import { ${resolved.className}${!isIProviderImported ? ', IProvider, ISqlProvider' : ''} } from '${resolved.importPath}';`);
|
|
150
|
+
}
|
|
278
151
|
if (!isIProviderImported)
|
|
279
152
|
isIProviderImported = true;
|
|
280
|
-
|
|
281
|
-
providerInitLines.push(` ${provName}: new ${className}((() => {
|
|
153
|
+
providerInitLines.push(` ${provName}: new ${resolved.className}((() => {
|
|
282
154
|
const raw = process.env.${provName.toUpperCase()} || '';
|
|
283
155
|
try { return raw ? JSON.parse(raw) : undefined; } catch { return raw; }
|
|
284
156
|
})())`);
|
|
@@ -286,42 +158,62 @@ async function handleGenerateAll(yamlPathArg, _outArg, moduleName, opts) {
|
|
|
286
158
|
}
|
|
287
159
|
}
|
|
288
160
|
else {
|
|
289
|
-
|
|
290
|
-
|
|
161
|
+
if (!appTs.includes("from '@currentjs/provider-mysql'")) {
|
|
162
|
+
importLines.push(`import { ProviderMysql, IProvider, ISqlProvider } from '@currentjs/provider-mysql';`);
|
|
163
|
+
}
|
|
291
164
|
providerInitLines.push(` mysql: new ProviderMysql((() => {
|
|
292
165
|
const raw = process.env.MYSQL || '';
|
|
293
166
|
try { return raw ? JSON.parse(raw) : undefined; } catch { return raw; }
|
|
294
167
|
})())`);
|
|
295
168
|
providersArrayEntries.push('mysql');
|
|
296
169
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
170
|
+
// Resolve database keys to provider variable names
|
|
171
|
+
const providerKeysSet = new Set(providersArrayEntries);
|
|
172
|
+
const allDatabaseKeys = [...new Set(moduleScans.map(s => s.databaseKey))];
|
|
173
|
+
const dbVarByKey = {};
|
|
174
|
+
const dbLines = [`const providers: Record<string, IProvider> = {\n${providerInitLines.join(',\n')}\n};`];
|
|
175
|
+
const emittedProviderKeys = new Set();
|
|
176
|
+
for (const key of allDatabaseKeys) {
|
|
177
|
+
const resolvedKey = providerKeysSet.has(key)
|
|
178
|
+
? key
|
|
179
|
+
: (defaultDatabaseKey && providerKeysSet.has(defaultDatabaseKey) ? defaultDatabaseKey : providersArrayEntries[0]);
|
|
180
|
+
if (!providerKeysSet.has(key)) {
|
|
181
|
+
// eslint-disable-next-line no-console
|
|
182
|
+
console.warn(colors_1.colors.yellow(`Module uses database '${key}' which is not in providers; using '${resolvedKey}'`));
|
|
305
183
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
for (const init of controllerInits) {
|
|
312
|
-
const maybe = [init.importStore, init.importService];
|
|
313
|
-
// Only add controller import if entity has a controller
|
|
314
|
-
if (init.importController && init.importController.trim() !== '') {
|
|
315
|
-
maybe.push(init.importController);
|
|
184
|
+
const varName = 'db' + (resolvedKey.charAt(0).toUpperCase() + resolvedKey.slice(1));
|
|
185
|
+
dbVarByKey[key] = varName;
|
|
186
|
+
if (!emittedProviderKeys.has(resolvedKey)) {
|
|
187
|
+
emittedProviderKeys.add(resolvedKey);
|
|
188
|
+
dbLines.push(`const ${varName} = providers['${resolvedKey}'] as ISqlProvider;`);
|
|
316
189
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
190
|
+
}
|
|
191
|
+
// --- Build DI instantiation order ---
|
|
192
|
+
const providerVarByType = new Map();
|
|
193
|
+
providerVarByType.set('ISqlProvider', (_c = dbVarByKey[allDatabaseKeys[0]]) !== null && _c !== void 0 ? _c : 'dbMysql');
|
|
194
|
+
// Per-class provider var: map each class to its module's database variable
|
|
195
|
+
const classProviderVar = new Map();
|
|
196
|
+
for (const scan of moduleScans) {
|
|
197
|
+
const dbVar = (_d = dbVarByKey[scan.databaseKey]) !== null && _d !== void 0 ? _d : dbVarByKey[Object.keys(dbVarByKey)[0]];
|
|
198
|
+
const classes = allClasses.filter(c => c.filePath.startsWith(scan.moduleDir + path.sep));
|
|
199
|
+
for (const cls of classes) {
|
|
200
|
+
const hasProviderParam = cls.constructorParams.some(p => providerVarByType.has(p.type));
|
|
201
|
+
if (hasProviderParam) {
|
|
202
|
+
classProviderVar.set(cls.className, dbVar);
|
|
322
203
|
}
|
|
323
204
|
}
|
|
324
205
|
}
|
|
206
|
+
const steps = (0, diResolver_1.buildInstantiationOrder)(allClasses, providerVarByType, classProviderVar, srcDir);
|
|
207
|
+
// --- Generate import lines for all discovered classes ---
|
|
208
|
+
for (const step of steps) {
|
|
209
|
+
const importLine = `import { ${step.className} } from '${step.importPath}';`;
|
|
210
|
+
if (!appTs.includes(importLine) && !importLines.includes(importLine)) {
|
|
211
|
+
importLines.push(importLine);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (!appTs.includes("from '@currentjs/router'")) {
|
|
215
|
+
importLines.push(`import { createWebServer } from '@currentjs/router';`);
|
|
216
|
+
}
|
|
325
217
|
if (importLines.length) {
|
|
326
218
|
const existingImports = new Set();
|
|
327
219
|
const importRegex = /^import[^;]+;$/gm;
|
|
@@ -331,63 +223,27 @@ async function handleGenerateAll(yamlPathArg, _outArg, moduleName, opts) {
|
|
|
331
223
|
if (toAdd.length)
|
|
332
224
|
appTs = toAdd.join('\n') + '\n' + appTs;
|
|
333
225
|
}
|
|
334
|
-
//
|
|
226
|
+
// --- Build wiring block ---
|
|
335
227
|
const wiringLines = [];
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const processed = new Set();
|
|
343
|
-
const addEntity = (init) => {
|
|
344
|
-
if (processed.has(init.entityName))
|
|
345
|
-
return;
|
|
346
|
-
// Process dependencies first
|
|
347
|
-
if (init.storeDependencies) {
|
|
348
|
-
for (const depName of init.storeDependencies) {
|
|
349
|
-
const depInit = uniqueInits.find(i => i.entityName === depName);
|
|
350
|
-
if (depInit && !processed.has(depName)) {
|
|
351
|
-
addEntity(depInit);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
sorted.push(init);
|
|
356
|
-
processed.add(init.entityName);
|
|
357
|
-
};
|
|
358
|
-
uniqueInits.forEach(init => addEntity(init));
|
|
359
|
-
// Generate wiring for stores and services
|
|
360
|
-
for (const init of sorted) {
|
|
361
|
-
const entityVar = init.entityVar;
|
|
362
|
-
const entityName = init.entityName;
|
|
363
|
-
// Build store constructor parameters
|
|
364
|
-
const storeParams = ['db'];
|
|
365
|
-
if (init.storeDependencies && init.storeDependencies.length > 0) {
|
|
366
|
-
init.storeDependencies.forEach(depName => {
|
|
367
|
-
const depVar = depName.charAt(0).toLowerCase() + depName.slice(1);
|
|
368
|
-
storeParams.push(`${depVar}Store`);
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
// Build service constructor parameters
|
|
372
|
-
const serviceParams = [`${entityVar}Store`];
|
|
373
|
-
if (init.serviceDependencies && init.serviceDependencies.length > 0) {
|
|
374
|
-
init.serviceDependencies.forEach(depName => {
|
|
375
|
-
const depVar = depName.charAt(0).toLowerCase() + depName.slice(1);
|
|
376
|
-
serviceParams.push(`${depVar}Store`);
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
wiringLines.push(`const ${entityVar}Store = new ${entityName}Store(${storeParams.join(', ')});`);
|
|
380
|
-
wiringLines.push(`const ${entityVar}Service = new ${entityName}Service(${serviceParams.join(', ')});`);
|
|
228
|
+
wiringLines.push(dbLines.join('\n'));
|
|
229
|
+
const nonControllers = steps.filter(s => !s.isController);
|
|
230
|
+
const controllers = steps.filter(s => s.isController);
|
|
231
|
+
for (const step of nonControllers) {
|
|
232
|
+
const args = step.constructorArgs.length > 0 ? step.constructorArgs.join(', ') : '';
|
|
233
|
+
wiringLines.push(`const ${step.varName} = new ${step.className}(${args});`);
|
|
381
234
|
}
|
|
382
|
-
// Only include registrations for entities with controllers
|
|
383
|
-
const registrations = controllerInits
|
|
384
|
-
.filter(i => i.registration && i.registration.trim() !== '')
|
|
385
|
-
.map(i => i.registration);
|
|
386
235
|
wiringLines.push('const controllers = [');
|
|
387
|
-
if (
|
|
388
|
-
|
|
236
|
+
if (controllers.length > 0) {
|
|
237
|
+
const registrations = controllers
|
|
238
|
+
.map(s => {
|
|
239
|
+
const args = s.constructorArgs.length > 0 ? s.constructorArgs.join(', ') : '';
|
|
240
|
+
return ` new ${s.className}(${args}),`;
|
|
241
|
+
})
|
|
242
|
+
.join('\n');
|
|
243
|
+
wiringLines.push(registrations);
|
|
389
244
|
}
|
|
390
245
|
wiringLines.push('];');
|
|
246
|
+
// Replace content between markers
|
|
391
247
|
const startMarker = constants_1.GENERATOR_MARKERS.CONTROLLERS_START;
|
|
392
248
|
const endMarker = constants_1.GENERATOR_MARKERS.CONTROLLERS_END;
|
|
393
249
|
const startIdx = appTs.indexOf(startMarker);
|
|
@@ -398,35 +254,29 @@ async function handleGenerateAll(yamlPathArg, _outArg, moduleName, opts) {
|
|
|
398
254
|
const block = '\n' + wiringLines.join('\n') + '\n';
|
|
399
255
|
appTs = before + block + after;
|
|
400
256
|
}
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const suffix = inner.endsWith(trimmed) ? '' : inner.slice(inner.indexOf(trimmed) + trimmed.length);
|
|
411
|
-
const sep = trimmed.length ? ', ' : '';
|
|
412
|
-
return full.replace(inner, `${prefix}${trimmed}${sep}renderer` + `: renderer${suffix}`);
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
else {
|
|
416
|
-
// Case 2: createWebServer(controllers)
|
|
417
|
-
const noOptionsRegex = /createWebServer\(\s*controllers\s*\)/m;
|
|
418
|
-
if (noOptionsRegex.test(appTs)) {
|
|
419
|
-
appTs = appTs.replace(noOptionsRegex, 'createWebServer(controllers, { renderer })');
|
|
257
|
+
// Deduplicate import lines across the entire file
|
|
258
|
+
const lines = appTs.split('\n');
|
|
259
|
+
const seenImports = new Set();
|
|
260
|
+
const deduped = lines.filter(line => {
|
|
261
|
+
const trimmed = line.trim();
|
|
262
|
+
if (/^import\s+/.test(trimmed) && trimmed.endsWith(';')) {
|
|
263
|
+
if (seenImports.has(trimmed))
|
|
264
|
+
return false;
|
|
265
|
+
seenImports.add(trimmed);
|
|
420
266
|
}
|
|
421
|
-
|
|
267
|
+
return true;
|
|
268
|
+
});
|
|
269
|
+
appTs = deduped.join('\n');
|
|
422
270
|
fs.writeFileSync(appTsPath, appTs, 'utf8');
|
|
271
|
+
// eslint-disable-next-line no-console
|
|
272
|
+
console.log(colors_1.colors.green(`Updated ${appTsPath}`));
|
|
423
273
|
}
|
|
424
274
|
catch (e) {
|
|
425
275
|
// eslint-disable-next-line no-console
|
|
426
|
-
console.warn(colors_1.colors.yellow(`Could not update app.ts
|
|
276
|
+
console.warn(colors_1.colors.yellow(`Could not update app.ts: ${e instanceof Error ? e.message : String(e)}`));
|
|
427
277
|
}
|
|
428
278
|
}
|
|
429
|
-
// Run
|
|
279
|
+
// Run build
|
|
430
280
|
(0, cliUtils_1.runCommand)('npm run build', {
|
|
431
281
|
infoMessage: '\nBuilding...',
|
|
432
282
|
successMessage: '[v] Build completed successfully',
|
|
@@ -51,12 +51,13 @@ function collectModelsFromYaml(yamlPath) {
|
|
|
51
51
|
allModels.push(...config.models);
|
|
52
52
|
sources.push(`app.yaml (${config.models.length} model(s))`);
|
|
53
53
|
}
|
|
54
|
-
//
|
|
55
|
-
if (config.modules && Array.isArray(config.modules)) {
|
|
54
|
+
// App YAML: modules as Record<string, { path }> — resolve path and read module YAML for .models
|
|
55
|
+
if (config.modules && typeof config.modules === 'object' && !Array.isArray(config.modules)) {
|
|
56
56
|
let moduleCount = 0;
|
|
57
|
-
config.modules
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
for (const entry of Object.values(config.modules)) {
|
|
58
|
+
const modulePath = entry.path;
|
|
59
|
+
if (!modulePath)
|
|
60
|
+
continue;
|
|
60
61
|
const moduleYamlPath = path.isAbsolute(modulePath)
|
|
61
62
|
? modulePath
|
|
62
63
|
: path.resolve(projectRoot, modulePath);
|
|
@@ -68,20 +69,7 @@ function collectModelsFromYaml(yamlPath) {
|
|
|
68
69
|
moduleCount++;
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
|
-
});
|
|
72
|
-
if (moduleCount > 0) {
|
|
73
|
-
sources.push(`app.yaml modules section (${moduleCount} module(s))`);
|
|
74
72
|
}
|
|
75
|
-
}
|
|
76
|
-
// Check if modules is an object (legacy format with models embedded)
|
|
77
|
-
else if (config.modules && typeof config.modules === 'object' && !Array.isArray(config.modules)) {
|
|
78
|
-
let moduleCount = 0;
|
|
79
|
-
Object.values(config.modules).forEach(moduleConfig => {
|
|
80
|
-
if (moduleConfig.models) {
|
|
81
|
-
allModels.push(...moduleConfig.models);
|
|
82
|
-
moduleCount++;
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
73
|
if (moduleCount > 0) {
|
|
86
74
|
sources.push(`app.yaml modules section (${moduleCount} module(s))`);
|
|
87
75
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function handleMigratePush(yamlPath?: string): Promise<void>;
|