@memberjunction/codegen-lib 5.4.1 → 5.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/README.md +65 -2
- package/dist/Angular/angular-codegen.d.ts.map +1 -1
- package/dist/Angular/angular-codegen.js +26 -12
- package/dist/Angular/angular-codegen.js.map +1 -1
- package/dist/Angular/related-entity-components.js +2 -2
- package/dist/Angular/related-entity-components.js.map +1 -1
- package/dist/Config/config.d.ts +10 -0
- package/dist/Config/config.d.ts.map +1 -1
- package/dist/Config/config.js +10 -0
- package/dist/Config/config.js.map +1 -1
- package/dist/Database/codeGenDatabaseProvider.d.ts +544 -0
- package/dist/Database/codeGenDatabaseProvider.d.ts.map +1 -0
- package/dist/Database/codeGenDatabaseProvider.js +29 -0
- package/dist/Database/codeGenDatabaseProvider.js.map +1 -0
- package/dist/Database/manage-metadata.d.ts +165 -60
- package/dist/Database/manage-metadata.d.ts.map +1 -1
- package/dist/Database/manage-metadata.js +592 -483
- package/dist/Database/manage-metadata.js.map +1 -1
- package/dist/Database/providers/postgresql/PostgreSQLCodeGenConnection.d.ts +53 -0
- package/dist/Database/providers/postgresql/PostgreSQLCodeGenConnection.d.ts.map +1 -0
- package/dist/Database/providers/postgresql/PostgreSQLCodeGenConnection.js +112 -0
- package/dist/Database/providers/postgresql/PostgreSQLCodeGenConnection.js.map +1 -0
- package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.d.ts +344 -0
- package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.d.ts.map +1 -0
- package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.js +1567 -0
- package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.js.map +1 -0
- package/dist/Database/providers/sqlserver/SQLServerCodeGenConnection.d.ts +42 -0
- package/dist/Database/providers/sqlserver/SQLServerCodeGenConnection.d.ts.map +1 -0
- package/dist/Database/providers/sqlserver/SQLServerCodeGenConnection.js +84 -0
- package/dist/Database/providers/sqlserver/SQLServerCodeGenConnection.js.map +1 -0
- package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.d.ts +372 -0
- package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.d.ts.map +1 -0
- package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.js +1483 -0
- package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.js.map +1 -0
- package/dist/Database/reorder-columns.d.ts +2 -2
- package/dist/Database/reorder-columns.d.ts.map +1 -1
- package/dist/Database/reorder-columns.js +9 -9
- package/dist/Database/reorder-columns.js.map +1 -1
- package/dist/Database/sql.d.ts +10 -5
- package/dist/Database/sql.d.ts.map +1 -1
- package/dist/Database/sql.js +44 -228
- package/dist/Database/sql.js.map +1 -1
- package/dist/Database/sql_codegen.d.ts +31 -29
- package/dist/Database/sql_codegen.d.ts.map +1 -1
- package/dist/Database/sql_codegen.js +209 -842
- package/dist/Database/sql_codegen.js.map +1 -1
- package/dist/Misc/action_subclasses_codegen.js +3 -2
- package/dist/Misc/action_subclasses_codegen.js.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.d.ts +4 -4
- package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
- package/dist/Misc/graphql_server_codegen.d.ts +6 -1
- package/dist/Misc/graphql_server_codegen.d.ts.map +1 -1
- package/dist/Misc/graphql_server_codegen.js +33 -35
- package/dist/Misc/graphql_server_codegen.js.map +1 -1
- package/dist/Misc/sql_logging.d.ts +2 -2
- package/dist/Misc/sql_logging.d.ts.map +1 -1
- package/dist/Misc/sql_logging.js +1 -1
- package/dist/Misc/sql_logging.js.map +1 -1
- package/dist/Misc/system_integrity.d.ts +6 -6
- package/dist/Misc/system_integrity.d.ts.map +1 -1
- package/dist/Misc/system_integrity.js +33 -8
- package/dist/Misc/system_integrity.js.map +1 -1
- package/dist/Misc/temp_batch_file.d.ts.map +1 -1
- package/dist/Misc/temp_batch_file.js +4 -1
- package/dist/Misc/temp_batch_file.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/runCodeGen.d.ts +30 -75
- package/dist/runCodeGen.d.ts.map +1 -1
- package/dist/runCodeGen.js +123 -215
- package/dist/runCodeGen.js.map +1 -1
- package/package.json +18 -15
package/dist/runCodeGen.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Coordinates all aspects of code generation including database schema analysis,
|
|
4
4
|
* metadata management, SQL generation, TypeScript entities, Angular components,
|
|
5
5
|
* GraphQL resolvers, and more.
|
|
6
|
+
*
|
|
7
|
+
* Supports both SQL Server and PostgreSQL database platforms via the dbType configuration.
|
|
6
8
|
*/
|
|
7
9
|
import { GraphQLServerGeneratorBase } from './Misc/graphql_server_codegen.js';
|
|
8
10
|
import { SQLCodeGenBase } from './Database/sql_codegen.js';
|
|
@@ -10,7 +12,7 @@ import { EntitySubClassGeneratorBase } from './Misc/entity_subclasses_codegen.js
|
|
|
10
12
|
import { UserCache, setupSQLServerClient } from '@memberjunction/sqlserver-dataprovider';
|
|
11
13
|
import { MSSQLConnection, sqlConfig } from './Config/db-connection.js';
|
|
12
14
|
import { ManageMetadataBase } from './Database/manage-metadata.js';
|
|
13
|
-
import { outputDir, commands, mj_core_schema, configInfo, getSettingValue } from './Config/config.js';
|
|
15
|
+
import { outputDir, commands, mj_core_schema, configInfo, getSettingValue, dbType } from './Config/config.js';
|
|
14
16
|
import { logError, logStatus, logWarning, startSpinner, updateSpinner, succeedSpinner, failSpinner, warnSpinner } from './Misc/status_logging.js';
|
|
15
17
|
import * as MJ from '@memberjunction/core';
|
|
16
18
|
import { RunCommandsBase } from './Misc/runCommand.js';
|
|
@@ -18,119 +20,116 @@ import { DBSchemaGeneratorBase } from './Database/dbSchema.js';
|
|
|
18
20
|
import { AngularClientGeneratorBase } from './Angular/angular-codegen.js';
|
|
19
21
|
import { SQLServerProviderConfigData } from '@memberjunction/sqlserver-dataprovider';
|
|
20
22
|
import { CreateNewUserBase } from './Misc/createNewUser.js';
|
|
21
|
-
import { MJGlobal } from '@memberjunction/global';
|
|
23
|
+
import { MJGlobal, UUIDsEqual } from '@memberjunction/global';
|
|
22
24
|
import { ActionSubClassGeneratorBase } from './Misc/action_subclasses_codegen.js';
|
|
23
25
|
import { SQLLogging } from './Misc/sql_logging.js';
|
|
26
|
+
import { SQLServerCodeGenConnection } from './Database/providers/sqlserver/SQLServerCodeGenConnection.js';
|
|
27
|
+
import { PostgreSQLCodeGenConnection } from './Database/providers/postgresql/PostgreSQLCodeGenConnection.js';
|
|
28
|
+
import { PostgreSQLDataProvider, PostgreSQLProviderConfigData } from '@memberjunction/postgresql-dataprovider';
|
|
29
|
+
import pg from 'pg';
|
|
24
30
|
import { SystemIntegrityBase } from './Misc/system_integrity.js';
|
|
25
31
|
import { ActionEngineBase } from '@memberjunction/actions-base';
|
|
26
32
|
import { AIEngine } from '@memberjunction/aiengine';
|
|
33
|
+
import { SetProvider, UserInfo } from '@memberjunction/core';
|
|
27
34
|
// Import pre-built MJ class registrations manifest (covers all @memberjunction/* packages)
|
|
28
35
|
import '@memberjunction/server-bootstrap-lite/mj-class-registrations';
|
|
29
36
|
/** Extract core schema name from configuration */
|
|
30
37
|
const { mjCoreSchema } = configInfo;
|
|
31
38
|
/**
|
|
32
39
|
* Main orchestrator class for the MemberJunction code generation process.
|
|
33
|
-
*
|
|
34
|
-
* This class coordinates a comprehensive code generation pipeline that transforms
|
|
35
|
-
* database schemas into a complete, type-safe, full-stack application. The process includes:
|
|
36
|
-
*
|
|
37
|
-
* **Pipeline Steps:**
|
|
38
|
-
* 1. **Database Setup** - Initialize connections and metadata
|
|
39
|
-
* 2. **Metadata Management** - Analyze schema changes and update metadata
|
|
40
|
-
* 3. **SQL Generation** - Create views, procedures, and indexes
|
|
41
|
-
* 4. **TypeScript Entities** - Generate entity classes with validation
|
|
42
|
-
* 5. **Angular Components** - Create forms and UI components
|
|
43
|
-
* 6. **GraphQL Resolvers** - Generate API endpoints
|
|
44
|
-
* 7. **Action Classes** - Create business logic containers
|
|
45
|
-
* 8. **Documentation** - Generate schema JSON for AI/documentation
|
|
46
|
-
* 9. **Post-processing** - Run commands and integrity checks
|
|
47
|
-
*
|
|
48
|
-
* **Customization:**
|
|
49
|
-
* You can sub-class this class and override specific methods to customize
|
|
50
|
-
* the code generation process for your specific needs.
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* ```typescript
|
|
54
|
-
* const codeGen = new RunCodeGenBase();
|
|
55
|
-
* await codeGen.Run(); // Full generation
|
|
56
|
-
* await codeGen.Run(true); // Skip database operations
|
|
57
|
-
* ```
|
|
58
40
|
*/
|
|
59
41
|
export class RunCodeGenBase {
|
|
60
42
|
/**
|
|
61
|
-
* Sets up the
|
|
62
|
-
*
|
|
63
|
-
* that will be used throughout the code generation process.
|
|
64
|
-
*
|
|
65
|
-
* Override this method to customize the data source setup process for different
|
|
66
|
-
* database providers or connection configurations.
|
|
67
|
-
*
|
|
68
|
-
* @returns Promise resolving to the configured SQLServerDataProvider instance
|
|
69
|
-
* @throws Error if connection setup fails
|
|
43
|
+
* Sets up the data source based on the configured database type (SQL Server or PostgreSQL).
|
|
44
|
+
* Initializes the appropriate data provider, connection, and user cache.
|
|
70
45
|
*/
|
|
71
46
|
async setupDataSource() {
|
|
72
|
-
/****************************************************************************************
|
|
73
|
-
// First, setup the data source and make sure the metadata and related stuff for MJCore is initialized
|
|
74
|
-
****************************************************************************************/
|
|
75
47
|
startSpinner('Initializing database connection...');
|
|
76
|
-
const
|
|
48
|
+
const platform = dbType();
|
|
49
|
+
if (platform === 'postgresql') {
|
|
50
|
+
return this.setupPostgreSQLDataSource();
|
|
51
|
+
}
|
|
52
|
+
return this.setupSQLServerDataSource();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Sets up SQL Server data source (original behavior).
|
|
56
|
+
*/
|
|
57
|
+
async setupSQLServerDataSource() {
|
|
58
|
+
const pool = await MSSQLConnection();
|
|
77
59
|
const config = new SQLServerProviderConfigData(pool, mj_core_schema());
|
|
78
|
-
const
|
|
79
|
-
|
|
60
|
+
const provider = await setupSQLServerClient(config);
|
|
61
|
+
const conn = new SQLServerCodeGenConnection(pool);
|
|
80
62
|
let connectionInfo = sqlConfig.server;
|
|
81
|
-
if (sqlConfig.port)
|
|
82
|
-
connectionInfo +=
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
63
|
+
if (sqlConfig.port)
|
|
64
|
+
connectionInfo += ':' + sqlConfig.port;
|
|
65
|
+
if (sqlConfig.options?.instanceName)
|
|
66
|
+
connectionInfo += '\\' + sqlConfig.options.instanceName;
|
|
67
|
+
connectionInfo += '/' + sqlConfig.database;
|
|
68
|
+
await UserCache.Instance.Refresh(pool);
|
|
69
|
+
const userMatch = UserCache.Users.find((u) => u?.Type?.trim().toLowerCase() === 'owner');
|
|
70
|
+
const currentUser = userMatch ? userMatch : UserCache.Users[0];
|
|
71
|
+
succeedSpinner('SQL Server connection initialized: ' + connectionInfo);
|
|
72
|
+
return { provider, connection: conn, currentUser, connectionInfo };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Sets up PostgreSQL data source.
|
|
76
|
+
*/
|
|
77
|
+
async setupPostgreSQLDataSource() {
|
|
78
|
+
const pgHost = process.env.PG_HOST ?? configInfo.dbHost;
|
|
79
|
+
const pgPort = parseInt(process.env.PG_PORT ?? String(configInfo.dbPort), 10) || 5432;
|
|
80
|
+
const pgDatabase = process.env.PG_DATABASE ?? configInfo.dbDatabase;
|
|
81
|
+
const pgUser = process.env.PG_USERNAME ?? configInfo.codeGenLogin;
|
|
82
|
+
const pgPassword = process.env.PG_PASSWORD ?? configInfo.codeGenPassword;
|
|
83
|
+
const coreSchema = mj_core_schema();
|
|
84
|
+
const pool = new pg.Pool({
|
|
85
|
+
host: pgHost,
|
|
86
|
+
port: pgPort,
|
|
87
|
+
database: pgDatabase,
|
|
88
|
+
user: pgUser,
|
|
89
|
+
password: pgPassword,
|
|
90
|
+
max: 20,
|
|
91
|
+
});
|
|
92
|
+
// Test connection
|
|
93
|
+
const client = await pool.connect();
|
|
94
|
+
client.release();
|
|
95
|
+
// Configure the PostgreSQL data provider
|
|
96
|
+
const pgConfig = new PostgreSQLProviderConfigData({ Host: pgHost, Port: pgPort, Database: pgDatabase, User: pgUser, Password: pgPassword }, coreSchema, 1 // checkRefreshIntervalSeconds: must be > 0 to trigger initial metadata load
|
|
97
|
+
);
|
|
98
|
+
const provider = new PostgreSQLDataProvider();
|
|
99
|
+
await provider.Config(pgConfig);
|
|
100
|
+
SetProvider(provider);
|
|
101
|
+
const conn = new PostgreSQLCodeGenConnection(pool);
|
|
102
|
+
// Load users (PostgreSQL version - query views directly)
|
|
103
|
+
const usersResult = await conn.query('SELECT * FROM "' + coreSchema + '"."vwUsers"');
|
|
104
|
+
const rolesResult = await conn.query('SELECT * FROM "' + coreSchema + '"."vwUserRoles"');
|
|
105
|
+
const userInfos = usersResult.recordset.map((user) => {
|
|
106
|
+
user.UserRoles = rolesResult.recordset.filter((role) => UUIDsEqual(role.UserID, user.ID));
|
|
107
|
+
return new UserInfo(provider, user);
|
|
108
|
+
});
|
|
109
|
+
const userMatch = userInfos.find((u) => u?.Type?.trim().toLowerCase() === 'owner');
|
|
110
|
+
const currentUser = userMatch ?? userInfos[0];
|
|
111
|
+
if (!currentUser) {
|
|
112
|
+
throw new Error('No users found in PostgreSQL. Ensure vwUsers has at least one user.');
|
|
86
113
|
}
|
|
87
|
-
connectionInfo
|
|
88
|
-
succeedSpinner(
|
|
89
|
-
return
|
|
114
|
+
const connectionInfo = pgHost + ':' + pgPort + '/' + pgDatabase;
|
|
115
|
+
succeedSpinner('PostgreSQL connection initialized: ' + connectionInfo);
|
|
116
|
+
return { provider, connection: conn, currentUser, connectionInfo };
|
|
90
117
|
}
|
|
91
118
|
/**
|
|
92
119
|
* Main entry point for the complete code generation process.
|
|
93
|
-
*
|
|
94
|
-
* Orchestrates the entire pipeline from database schema analysis to final code output.
|
|
95
|
-
* The process is highly configurable through the configuration file and can be
|
|
96
|
-
* partially skipped for faster iteration during development.
|
|
97
|
-
*
|
|
98
|
-
* **Process Flow:**
|
|
99
|
-
* 1. Initialize data sources and user context
|
|
100
|
-
* 2. Execute pre-generation commands and scripts
|
|
101
|
-
* 3. Manage metadata and schema changes
|
|
102
|
-
* 4. Generate SQL objects (views, procedures, indexes)
|
|
103
|
-
* 5. Generate TypeScript entity classes
|
|
104
|
-
* 6. Generate Angular UI components
|
|
105
|
-
* 7. Generate GraphQL API resolvers
|
|
106
|
-
* 8. Generate Action business logic classes
|
|
107
|
-
* 9. Create documentation JSON
|
|
108
|
-
* 10. Run integrity checks
|
|
109
|
-
* 11. Execute post-generation commands
|
|
110
|
-
*
|
|
111
|
-
* @param skipDatabaseGeneration If true, skips all database-related operations
|
|
112
|
-
* (metadata management, SQL generation). Useful for faster UI-only regeneration.
|
|
113
|
-
* @throws Error if any critical step fails
|
|
114
|
-
* @returns Promise that resolves when generation is complete
|
|
115
120
|
*/
|
|
116
121
|
async Run(skipDatabaseGeneration = false) {
|
|
117
122
|
try {
|
|
118
123
|
const startTime = new Date();
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const pool = await MSSQLConnection();
|
|
123
|
-
await UserCache.Instance.Refresh(pool);
|
|
124
|
-
const userMatch = UserCache.Users.find((u) => u?.Type?.trim().toLowerCase() === 'owner');
|
|
125
|
-
const currentUser = userMatch ? userMatch : UserCache.Users[0]; // if we don't find an Owner, use the first user in the cache
|
|
126
|
-
// get the entity metadata
|
|
124
|
+
const platform = dbType();
|
|
125
|
+
startSpinner('Starting MemberJunction CodeGen (' + platform + ') @ ' + startTime.toLocaleString());
|
|
126
|
+
const { provider, connection: conn, currentUser } = await this.setupDataSource();
|
|
127
127
|
const md = new MJ.Metadata();
|
|
128
128
|
if (md.Entities.length === 0) {
|
|
129
129
|
failSpinner('No entities found in metadata');
|
|
130
130
|
process.exit(1);
|
|
131
131
|
}
|
|
132
|
-
succeedSpinner(
|
|
133
|
-
// Initialize AIEngine for advanced generation features (needed for prompt metadata access)
|
|
132
|
+
succeedSpinner('Loaded ' + md.Entities.length + ' entities from metadata');
|
|
134
133
|
if (configInfo.advancedGeneration?.enableAdvancedGeneration) {
|
|
135
134
|
startSpinner('Initializing AI Engine for advanced generation...');
|
|
136
135
|
await AIEngine.Instance.Config(false, currentUser);
|
|
@@ -138,14 +137,10 @@ export class RunCodeGenBase {
|
|
|
138
137
|
}
|
|
139
138
|
const runCommandsObject = MJGlobal.Instance.ClassFactory.CreateInstance(RunCommandsBase);
|
|
140
139
|
const sqlCodeGenObject = MJGlobal.Instance.ClassFactory.CreateInstance(SQLCodeGenBase);
|
|
141
|
-
// check to see if the user wants to skip database generation via the config settings
|
|
142
140
|
const skipDB = skipDatabaseGeneration || getSettingValue('skip_database_generation', false);
|
|
143
141
|
if (!skipDB) {
|
|
144
142
|
startSpinner('Handling SQL Script Execution, Metadata Maintenance, and SQL Object Generation...');
|
|
145
|
-
SQLLogging.initSQLLogging();
|
|
146
|
-
/****************************************************************************************
|
|
147
|
-
// STEP 0 --- Precursor Step execute any commands specified in the config file
|
|
148
|
-
****************************************************************************************/
|
|
143
|
+
SQLLogging.initSQLLogging();
|
|
149
144
|
const beforeCommands = commands('BEFORE');
|
|
150
145
|
if (beforeCommands && beforeCommands.length > 0) {
|
|
151
146
|
updateSpinner('Executing BEFORE commands...');
|
|
@@ -153,15 +148,9 @@ export class RunCodeGenBase {
|
|
|
153
148
|
if (results.some((r) => !r.success))
|
|
154
149
|
logError('ERROR running one or more BEFORE commands');
|
|
155
150
|
}
|
|
156
|
-
/****************************************************************************************
|
|
157
|
-
// STEP 0.1 --- Execute any before SQL Scripts specified in the config file
|
|
158
|
-
****************************************************************************************/
|
|
159
151
|
updateSpinner('Executing before-all SQL Scripts...');
|
|
160
|
-
if (!(await sqlCodeGenObject.runCustomSQLScripts(
|
|
152
|
+
if (!(await sqlCodeGenObject.runCustomSQLScripts(conn, 'before-all')))
|
|
161
153
|
logError('ERROR running before-all SQL Scripts');
|
|
162
|
-
/****************************************************************************************
|
|
163
|
-
// STEP 0.2 --- Create a new user if there is newUserSetup info in the config file
|
|
164
|
-
****************************************************************************************/
|
|
165
154
|
const newUserSetup = configInfo.newUserSetup;
|
|
166
155
|
if (newUserSetup) {
|
|
167
156
|
updateSpinner('Setting up new user...');
|
|
@@ -177,33 +166,31 @@ export class RunCodeGenBase {
|
|
|
177
166
|
}
|
|
178
167
|
}
|
|
179
168
|
}
|
|
180
|
-
/****************************************************************************************
|
|
181
|
-
// STEP 1 - Manage Metadata - including generating new metadata as required
|
|
182
|
-
****************************************************************************************/
|
|
183
169
|
const manageMD = MJGlobal.Instance.ClassFactory.CreateInstance(ManageMetadataBase);
|
|
184
170
|
updateSpinner('Managing Metadata...');
|
|
185
|
-
const metadataSuccess = await manageMD.manageMetadata(
|
|
171
|
+
const metadataSuccess = await manageMD.manageMetadata(conn, currentUser);
|
|
186
172
|
if (!metadataSuccess) {
|
|
187
173
|
failSpinner('ERROR managing metadata');
|
|
188
174
|
}
|
|
189
175
|
else {
|
|
190
|
-
// now - we need to tell our metadata object to refresh itself
|
|
191
176
|
await provider.Refresh();
|
|
192
177
|
succeedSpinner('Metadata management completed');
|
|
193
178
|
}
|
|
194
|
-
/****************************************************************************************
|
|
195
|
-
// STEP 2 - SQL Script Generation
|
|
196
|
-
****************************************************************************************/
|
|
197
179
|
const sqlOutputDir = outputDir('SQL', true);
|
|
198
180
|
if (sqlOutputDir) {
|
|
199
181
|
startSpinner('Managing SQL Scripts and Execution...');
|
|
200
|
-
const sqlSuccess = await sqlCodeGenObject.manageSQLScriptsAndExecution(
|
|
182
|
+
const sqlSuccess = await sqlCodeGenObject.manageSQLScriptsAndExecution(conn, md.Entities, sqlOutputDir, currentUser);
|
|
201
183
|
if (!sqlSuccess) {
|
|
202
184
|
failSpinner('Error managing SQL scripts and execution');
|
|
203
185
|
}
|
|
204
186
|
else {
|
|
205
187
|
succeedSpinner('SQL scripts and execution completed');
|
|
206
188
|
}
|
|
189
|
+
// Refresh metadata again after SQL scripts execution because the stale
|
|
190
|
+
// entity relationship cleanup (step 3.5) runs inside manageSQLScriptsAndExecution,
|
|
191
|
+
// AFTER the entity field sync. The first refresh (above) loaded metadata before
|
|
192
|
+
// those DB changes, so the in-memory metadata still has stale relationships.
|
|
193
|
+
await provider.Refresh();
|
|
207
194
|
}
|
|
208
195
|
else {
|
|
209
196
|
warnSpinner('SQL output directory NOT found in config file, skipping...');
|
|
@@ -211,26 +198,20 @@ export class RunCodeGenBase {
|
|
|
211
198
|
}
|
|
212
199
|
else {
|
|
213
200
|
warnSpinner('Skipping database generation (skip_database_generation = true)');
|
|
214
|
-
// we skipped the database generation but we need to load generated code for validators from the database to ensure that we have them
|
|
215
|
-
// ready for later use.
|
|
216
201
|
const manageMD = MJGlobal.Instance.ClassFactory.CreateInstance(ManageMetadataBase);
|
|
217
202
|
startSpinner('Checking/Loading AI Generated Code from Metadata...');
|
|
218
|
-
const metadataSuccess = await manageMD.loadGeneratedCode(
|
|
203
|
+
const metadataSuccess = await manageMD.loadGeneratedCode(conn, currentUser);
|
|
219
204
|
if (!metadataSuccess) {
|
|
220
205
|
failSpinner('ERROR checking/loading AI Generated Code from Metadata');
|
|
221
|
-
return;
|
|
206
|
+
return;
|
|
222
207
|
}
|
|
223
208
|
else {
|
|
224
209
|
succeedSpinner('AI Generated Code loaded from Metadata');
|
|
225
210
|
}
|
|
226
211
|
}
|
|
227
|
-
// Apply excludeSchemas filter to all entities before splitting into core/non-core
|
|
228
|
-
// This ensures TypeScript, Angular, and GraphQL generators respect schema exclusions
|
|
229
|
-
// (SQL generation already applies this filter internally in sql_codegen.ts)
|
|
230
212
|
const apiEntities = md.Entities.filter((e) => e.IncludeInAPI);
|
|
231
213
|
const excludedSchemaNames = configInfo.excludeSchemas.map(s => s.toLowerCase());
|
|
232
214
|
const includedEntities = apiEntities.filter((e) => !excludedSchemaNames.includes(e.SchemaName.trim().toLowerCase()));
|
|
233
|
-
// Log excluded schemas if any entities were filtered out
|
|
234
215
|
const excludedCount = apiEntities.length - includedEntities.length;
|
|
235
216
|
if (excludedCount > 0) {
|
|
236
217
|
const excludedBySchema = apiEntities
|
|
@@ -241,24 +222,17 @@ export class RunCodeGenBase {
|
|
|
241
222
|
return acc;
|
|
242
223
|
}, {});
|
|
243
224
|
const schemaDetails = Object.entries(excludedBySchema)
|
|
244
|
-
.map(([schema, count]) =>
|
|
225
|
+
.map(([schema, count]) => schema + ': ' + count)
|
|
245
226
|
.join(', ');
|
|
246
|
-
logStatus(
|
|
227
|
+
logStatus('Excluded ' + excludedCount + ' entities from code generation by schema: ' + schemaDetails);
|
|
247
228
|
}
|
|
248
229
|
const coreEntities = includedEntities.filter((e) => e.SchemaName.trim().toLowerCase() === mjCoreSchema.trim().toLowerCase());
|
|
249
230
|
const nonCoreEntities = includedEntities.filter((e) => e.SchemaName.trim().toLowerCase() !== mjCoreSchema.trim().toLowerCase());
|
|
250
|
-
// Check if we're in verbose mode to determine spinner behavior
|
|
251
231
|
const isVerbose = configInfo?.verboseOutput ?? false;
|
|
252
|
-
if (!isVerbose)
|
|
253
|
-
// In non-verbose mode, start a single spinner for all TypeScript generation
|
|
232
|
+
if (!isVerbose)
|
|
254
233
|
startSpinner('Generating TypeScript code...');
|
|
255
|
-
}
|
|
256
|
-
/****************************************************************************************
|
|
257
|
-
// STEP 3(a) - GraphQL Server Code Gen
|
|
258
|
-
****************************************************************************************/
|
|
259
234
|
const graphQLCoreResolversOutputDir = outputDir('GraphQLCoreEntityResolvers', false);
|
|
260
235
|
if (graphQLCoreResolversOutputDir) {
|
|
261
|
-
// generate the GraphQL server code
|
|
262
236
|
if (isVerbose)
|
|
263
237
|
startSpinner('Generating CORE Entity GraphQL Resolver Code...');
|
|
264
238
|
const graphQLGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(GraphQLServerGeneratorBase);
|
|
@@ -266,13 +240,11 @@ export class RunCodeGenBase {
|
|
|
266
240
|
failSpinner('Error generating GraphQL server code');
|
|
267
241
|
return;
|
|
268
242
|
}
|
|
269
|
-
else if (isVerbose)
|
|
243
|
+
else if (isVerbose)
|
|
270
244
|
succeedSpinner('CORE Entity GraphQL Resolver Code generated');
|
|
271
|
-
}
|
|
272
245
|
}
|
|
273
246
|
const graphqlOutputDir = outputDir('GraphQLServer', true);
|
|
274
247
|
if (graphqlOutputDir) {
|
|
275
|
-
// generate the GraphQL server code
|
|
276
248
|
if (isVerbose)
|
|
277
249
|
startSpinner('Generating GraphQL Resolver Code...');
|
|
278
250
|
const graphQLGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(GraphQLServerGeneratorBase);
|
|
@@ -281,56 +253,39 @@ export class RunCodeGenBase {
|
|
|
281
253
|
failSpinner('Error generating GraphQL Resolver code');
|
|
282
254
|
return;
|
|
283
255
|
}
|
|
284
|
-
else if (isVerbose)
|
|
256
|
+
else if (isVerbose)
|
|
285
257
|
succeedSpinner('GraphQL Resolver Code generated');
|
|
286
|
-
}
|
|
287
258
|
}
|
|
288
|
-
else if (isVerbose)
|
|
259
|
+
else if (isVerbose)
|
|
289
260
|
warnSpinner('GraphQL server output directory NOT found in config file, skipping...');
|
|
290
|
-
}
|
|
291
|
-
/****************************************************************************************
|
|
292
|
-
// STEP 4 - Core Entity Subclass Code Gen
|
|
293
|
-
****************************************************************************************/
|
|
294
261
|
const coreEntitySubClassOutputDir = outputDir('CoreEntitySubClasses', false);
|
|
295
262
|
if (coreEntitySubClassOutputDir && coreEntitySubClassOutputDir.length > 0) {
|
|
296
|
-
// generate the entity subclass code
|
|
297
263
|
if (isVerbose)
|
|
298
264
|
startSpinner('Generating CORE Entity Subclass Code...');
|
|
299
265
|
const entitySubClassGeneratorObject = MJGlobal.Instance.ClassFactory.CreateInstance(EntitySubClassGeneratorBase);
|
|
300
|
-
if (!await entitySubClassGeneratorObject.generateAllEntitySubClasses(
|
|
266
|
+
if (!await entitySubClassGeneratorObject.generateAllEntitySubClasses(conn, coreEntities, coreEntitySubClassOutputDir, skipDB)) {
|
|
301
267
|
failSpinner('Error generating entity subclass code');
|
|
302
268
|
return;
|
|
303
269
|
}
|
|
304
|
-
else if (isVerbose)
|
|
270
|
+
else if (isVerbose)
|
|
305
271
|
succeedSpinner('CORE Entity Subclass Code generated');
|
|
306
|
-
}
|
|
307
272
|
}
|
|
308
|
-
/****************************************************************************************
|
|
309
|
-
// STEP 4.1 - Entity Subclass Code Gen
|
|
310
|
-
****************************************************************************************/
|
|
311
273
|
const entitySubClassOutputDir = outputDir('EntitySubClasses', true);
|
|
312
274
|
if (entitySubClassOutputDir) {
|
|
313
|
-
// generate the entity subclass code
|
|
314
275
|
if (isVerbose)
|
|
315
276
|
startSpinner('Generating Entity Subclass Code...');
|
|
316
277
|
const entitySubClassGeneratorObject = MJGlobal.Instance.ClassFactory.CreateInstance(EntitySubClassGeneratorBase);
|
|
317
|
-
if (!await entitySubClassGeneratorObject.generateAllEntitySubClasses(
|
|
278
|
+
if (!await entitySubClassGeneratorObject.generateAllEntitySubClasses(conn, nonCoreEntities, entitySubClassOutputDir, skipDB)) {
|
|
318
279
|
failSpinner('Error generating entity subclass code');
|
|
319
280
|
return;
|
|
320
281
|
}
|
|
321
|
-
else if (isVerbose)
|
|
282
|
+
else if (isVerbose)
|
|
322
283
|
succeedSpinner('Entity Subclass Code generated');
|
|
323
|
-
}
|
|
324
284
|
}
|
|
325
|
-
else if (isVerbose)
|
|
285
|
+
else if (isVerbose)
|
|
326
286
|
warnSpinner('Entity subclass output directory NOT found in config file, skipping...');
|
|
327
|
-
}
|
|
328
|
-
/****************************************************************************************
|
|
329
|
-
// STEP 5 - Angular Code Gen
|
|
330
|
-
****************************************************************************************/
|
|
331
287
|
const angularCoreEntitiesOutputDir = outputDir('AngularCoreEntities', false);
|
|
332
288
|
if (angularCoreEntitiesOutputDir) {
|
|
333
|
-
// generate the Angular client code
|
|
334
289
|
if (isVerbose)
|
|
335
290
|
startSpinner('Generating Angular CORE Entities Code...');
|
|
336
291
|
const angularGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(AngularClientGeneratorBase);
|
|
@@ -338,13 +293,11 @@ export class RunCodeGenBase {
|
|
|
338
293
|
failSpinner('Error generating Angular CORE Entities code');
|
|
339
294
|
return;
|
|
340
295
|
}
|
|
341
|
-
else if (isVerbose)
|
|
296
|
+
else if (isVerbose)
|
|
342
297
|
succeedSpinner('Angular CORE Entities Code generated');
|
|
343
|
-
}
|
|
344
298
|
}
|
|
345
299
|
const angularOutputDir = outputDir('Angular', false);
|
|
346
300
|
if (angularOutputDir) {
|
|
347
|
-
// generate the Angular client code
|
|
348
301
|
if (isVerbose)
|
|
349
302
|
startSpinner('Generating Angular Code...');
|
|
350
303
|
const angularGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(AngularClientGeneratorBase);
|
|
@@ -352,37 +305,26 @@ export class RunCodeGenBase {
|
|
|
352
305
|
failSpinner('Error generating Angular code');
|
|
353
306
|
return;
|
|
354
307
|
}
|
|
355
|
-
else if (isVerbose)
|
|
308
|
+
else if (isVerbose)
|
|
356
309
|
succeedSpinner('Angular Code generated');
|
|
357
|
-
}
|
|
358
310
|
}
|
|
359
|
-
else if (isVerbose)
|
|
311
|
+
else if (isVerbose)
|
|
360
312
|
warnSpinner('Angular output directory NOT found in config file, skipping...');
|
|
361
|
-
}
|
|
362
|
-
/****************************************************************************************
|
|
363
|
-
// STEP 6 - Database Schema Output in JSON - for documentation and can be used by AI/etc.
|
|
364
|
-
****************************************************************************************/
|
|
365
313
|
const dbSchemaOutputDir = outputDir('DBSchemaJSON', false);
|
|
366
314
|
if (dbSchemaOutputDir) {
|
|
367
|
-
// generate the GraphQL client code
|
|
368
315
|
if (isVerbose)
|
|
369
316
|
startSpinner('Generating Database Schema JSON Output...');
|
|
370
317
|
const schemaGeneratorObject = MJGlobal.Instance.ClassFactory.CreateInstance(DBSchemaGeneratorBase);
|
|
371
318
|
if (!schemaGeneratorObject.generateDBSchemaJSONOutput(md.Entities, dbSchemaOutputDir)) {
|
|
372
319
|
failSpinner('Error generating Database Schema JSON Output, non-fatal, continuing...');
|
|
373
320
|
}
|
|
374
|
-
else if (isVerbose)
|
|
321
|
+
else if (isVerbose)
|
|
375
322
|
succeedSpinner('Database Schema JSON Output generated');
|
|
376
|
-
}
|
|
377
323
|
}
|
|
378
|
-
else if (isVerbose)
|
|
324
|
+
else if (isVerbose)
|
|
379
325
|
warnSpinner('DB Schema output directory NOT found in config file, skipping...');
|
|
380
|
-
}
|
|
381
|
-
/****************************************************************************************
|
|
382
|
-
// STEP 7 - Actions Code Gen
|
|
383
|
-
****************************************************************************************/
|
|
384
326
|
const coreActionsOutputDir = outputDir('CoreActionSubclasses', false);
|
|
385
|
-
await ActionEngineBase.Instance.Config(false, currentUser);
|
|
327
|
+
await ActionEngineBase.Instance.Config(false, currentUser);
|
|
386
328
|
if (coreActionsOutputDir) {
|
|
387
329
|
if (isVerbose)
|
|
388
330
|
startSpinner('Generating CORE Actions Code...');
|
|
@@ -391,9 +333,8 @@ export class RunCodeGenBase {
|
|
|
391
333
|
failSpinner('Error generating CORE Actions code');
|
|
392
334
|
return;
|
|
393
335
|
}
|
|
394
|
-
else if (isVerbose)
|
|
336
|
+
else if (isVerbose)
|
|
395
337
|
succeedSpinner('CORE Actions Code generated');
|
|
396
|
-
}
|
|
397
338
|
}
|
|
398
339
|
const actionsOutputDir = outputDir('ActionSubclasses', false);
|
|
399
340
|
if (actionsOutputDir) {
|
|
@@ -404,80 +345,47 @@ export class RunCodeGenBase {
|
|
|
404
345
|
failSpinner('Error generating Actions code');
|
|
405
346
|
return;
|
|
406
347
|
}
|
|
407
|
-
else if (isVerbose)
|
|
348
|
+
else if (isVerbose)
|
|
408
349
|
succeedSpinner('Actions Code generated');
|
|
409
|
-
}
|
|
410
350
|
}
|
|
411
|
-
else if (isVerbose)
|
|
351
|
+
else if (isVerbose)
|
|
412
352
|
warnSpinner('Actions output directory NOT found in config file, skipping...');
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
SQLLogging.finishSQLLogging(); // finish up the SQL Logging
|
|
416
|
-
// Complete the TypeScript generation spinner in non-verbose mode
|
|
417
|
-
if (!isVerbose) {
|
|
353
|
+
SQLLogging.finishSQLLogging();
|
|
354
|
+
if (!isVerbose)
|
|
418
355
|
succeedSpinner('TypeScript code generation completed');
|
|
419
|
-
}
|
|
420
|
-
// now run integrity checks
|
|
421
356
|
startSpinner('Running system integrity checks...');
|
|
422
|
-
await SystemIntegrityBase.RunIntegrityChecks(
|
|
357
|
+
await SystemIntegrityBase.RunIntegrityChecks(conn, true);
|
|
423
358
|
succeedSpinner('System integrity checks completed');
|
|
424
|
-
/****************************************************************************************
|
|
425
|
-
// STEP 8 --- Finalization Step - execute any AFTER commands specified in the config file
|
|
426
|
-
****************************************************************************************/
|
|
427
359
|
const afterCommands = commands('AFTER');
|
|
428
360
|
if (afterCommands && afterCommands.length > 0) {
|
|
429
361
|
startSpinner('Executing AFTER commands...');
|
|
430
362
|
const results = await runCommandsObject.runCommands(afterCommands);
|
|
431
|
-
if (results.some((r) => !r.success))
|
|
363
|
+
if (results.some((r) => !r.success))
|
|
432
364
|
failSpinner('ERROR running one or more AFTER commands');
|
|
433
|
-
|
|
434
|
-
else {
|
|
365
|
+
else
|
|
435
366
|
succeedSpinner('AFTER commands completed');
|
|
436
|
-
}
|
|
437
367
|
}
|
|
438
|
-
/****************************************************************************************
|
|
439
|
-
// STEP 9 --- Execute any AFTER SQL Scripts specified in the config file
|
|
440
|
-
****************************************************************************************/
|
|
441
368
|
if (!skipDB) {
|
|
442
369
|
startSpinner('Executing after-all SQL Scripts...');
|
|
443
|
-
if (!(await sqlCodeGenObject.runCustomSQLScripts(
|
|
370
|
+
if (!(await sqlCodeGenObject.runCustomSQLScripts(conn, 'after-all')))
|
|
444
371
|
failSpinner('ERROR running after-all SQL Scripts');
|
|
445
|
-
|
|
446
|
-
else {
|
|
372
|
+
else
|
|
447
373
|
succeedSpinner('After-all SQL Scripts completed');
|
|
448
|
-
}
|
|
449
374
|
}
|
|
450
375
|
const endTime = new Date();
|
|
451
376
|
const totalSeconds = (endTime.getTime() - startTime.getTime()) / 1000;
|
|
452
|
-
succeedSpinner(
|
|
453
|
-
process.exit(0);
|
|
377
|
+
succeedSpinner('MJ CodeGen Complete! ' + md.Entities.length + ' entities processed in ' + totalSeconds + 's @ ' + endTime.toLocaleString());
|
|
378
|
+
process.exit(0);
|
|
454
379
|
}
|
|
455
380
|
catch (e) {
|
|
456
|
-
failSpinner(
|
|
381
|
+
failSpinner('CodeGen failed: ' + e);
|
|
457
382
|
logError(e);
|
|
458
|
-
process.exit(1);
|
|
383
|
+
process.exit(1);
|
|
459
384
|
}
|
|
460
385
|
}
|
|
461
386
|
}
|
|
462
387
|
/**
|
|
463
388
|
* Convenience function to run the MemberJunction code generation process.
|
|
464
|
-
* Creates a new instance of RunCodeGenBase and executes the full generation pipeline.
|
|
465
|
-
*
|
|
466
|
-
* This is the recommended way to trigger code generation from external scripts
|
|
467
|
-
* or applications.
|
|
468
|
-
*
|
|
469
|
-
* @param skipDatabaseGeneration Whether to skip database-related operations
|
|
470
|
-
* @returns Promise that resolves when generation is complete
|
|
471
|
-
* @throws Error if generation fails
|
|
472
|
-
*
|
|
473
|
-
* @example
|
|
474
|
-
* ```typescript
|
|
475
|
-
* // Full generation
|
|
476
|
-
* await runMemberJunctionCodeGeneration();
|
|
477
|
-
*
|
|
478
|
-
* // Skip database operations for faster UI generation
|
|
479
|
-
* await runMemberJunctionCodeGeneration(true);
|
|
480
|
-
* ```
|
|
481
389
|
*/
|
|
482
390
|
export async function runMemberJunctionCodeGeneration(skipDatabaseGeneration = false) {
|
|
483
391
|
const runObject = MJGlobal.Instance.ClassFactory.CreateInstance(RunCodeGenBase);
|