@memberjunction/codegen-lib 6.1.0-edge.4 → 6.1.0-edge.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Config/config.d.ts +172 -10
- package/dist/Config/config.d.ts.map +1 -1
- package/dist/Config/config.js +57 -1
- package/dist/Config/config.js.map +1 -1
- package/dist/Config/schema-output.d.ts +33 -0
- package/dist/Config/schema-output.d.ts.map +1 -0
- package/dist/Config/schema-output.js +52 -0
- package/dist/Config/schema-output.js.map +1 -0
- package/dist/Database/codeGenDatabaseProvider.d.ts +49 -0
- package/dist/Database/codeGenDatabaseProvider.d.ts.map +1 -1
- package/dist/Database/codeGenDatabaseProvider.js +105 -0
- package/dist/Database/codeGenDatabaseProvider.js.map +1 -1
- package/dist/Database/exclude-tables.d.ts +26 -0
- package/dist/Database/exclude-tables.d.ts.map +1 -0
- package/dist/Database/exclude-tables.js +38 -0
- package/dist/Database/exclude-tables.js.map +1 -0
- package/dist/Database/manage-metadata.d.ts +52 -2
- package/dist/Database/manage-metadata.d.ts.map +1 -1
- package/dist/Database/manage-metadata.js +95 -14
- package/dist/Database/manage-metadata.js.map +1 -1
- package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.d.ts +2 -0
- package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.d.ts.map +1 -1
- package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.js +10 -0
- package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.js.map +1 -1
- package/dist/Database/providers/sqlserver/__tests__/SQLServerCodeGenProvider.test.js +17 -0
- package/dist/Database/providers/sqlserver/__tests__/SQLServerCodeGenProvider.test.js.map +1 -1
- package/dist/Database/sql_codegen.d.ts +15 -0
- package/dist/Database/sql_codegen.d.ts.map +1 -1
- package/dist/Database/sql_codegen.js +55 -15
- package/dist/Database/sql_codegen.js.map +1 -1
- package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts +22 -0
- package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts.map +1 -1
- package/dist/Manifest/GenerateClassRegistrationsManifest.js +112 -5
- package/dist/Manifest/GenerateClassRegistrationsManifest.js.map +1 -1
- package/dist/Misc/codegen-reporter.d.ts +3 -0
- package/dist/Misc/codegen-reporter.d.ts.map +1 -1
- package/dist/Misc/codegen-reporter.js +3 -0
- package/dist/Misc/codegen-reporter.js.map +1 -1
- package/dist/Misc/emit-stats.d.ts +28 -0
- package/dist/Misc/emit-stats.d.ts.map +1 -0
- package/dist/Misc/emit-stats.js +43 -0
- package/dist/Misc/emit-stats.js.map +1 -0
- package/dist/Misc/entity_subclasses_codegen.d.ts +20 -2
- package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.js +92 -33
- package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
- package/dist/Misc/file-write.d.ts +15 -0
- package/dist/Misc/file-write.d.ts.map +1 -0
- package/dist/Misc/file-write.js +33 -0
- package/dist/Misc/file-write.js.map +1 -0
- package/dist/Misc/graphql_server_codegen.d.ts +12 -2
- package/dist/Misc/graphql_server_codegen.d.ts.map +1 -1
- package/dist/Misc/graphql_server_codegen.js +61 -12
- package/dist/Misc/graphql_server_codegen.js.map +1 -1
- package/dist/Misc/runCommand.d.ts +5 -0
- package/dist/Misc/runCommand.d.ts.map +1 -1
- package/dist/Misc/runCommand.js +43 -17
- package/dist/Misc/runCommand.js.map +1 -1
- package/dist/Misc/schema-emit.d.ts +123 -0
- package/dist/Misc/schema-emit.d.ts.map +1 -0
- package/dist/Misc/schema-emit.js +220 -0
- package/dist/Misc/schema-emit.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/runCodeGen.d.ts +20 -2
- package/dist/runCodeGen.d.ts.map +1 -1
- package/dist/runCodeGen.js +147 -21
- package/dist/runCodeGen.js.map +1 -1
- package/package.json +29 -29
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema is the incremental unit for CodeGen file emit.
|
|
3
|
+
*
|
|
4
|
+
* A 2,000-entity database is almost never one schema with 2,000 tables. It is
|
|
5
|
+
* two-to-three dozen schemas of 100–150 tables each. Entity identity stays the
|
|
6
|
+
* entity (names, FKs, metadata rows). Everything that can be incremental —
|
|
7
|
+
* emit, dirty regen, parallelism, output-directory routing, agent context —
|
|
8
|
+
* keys off the schema.
|
|
9
|
+
*/
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { writeFileIfChanged } from './file-write.js';
|
|
13
|
+
/**
|
|
14
|
+
* Case-insensitive schema match. `%` is the only wildcard (SQL `_` is a
|
|
15
|
+
* literal — schema names like `bsd_crm` are common and must not glob).
|
|
16
|
+
*/
|
|
17
|
+
/** Case-insensitive schema/entity key. Null/undefined become ''. */
|
|
18
|
+
export function schemaKey(name) {
|
|
19
|
+
return (name ?? '').trim().toLowerCase();
|
|
20
|
+
}
|
|
21
|
+
export function schemaNameMatches(pattern, schemaName) {
|
|
22
|
+
const p = schemaKey(pattern);
|
|
23
|
+
const s = schemaKey(schemaName);
|
|
24
|
+
if (!p.includes('%')) {
|
|
25
|
+
return p === s;
|
|
26
|
+
}
|
|
27
|
+
const escaped = p.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/%/g, '.*');
|
|
28
|
+
return new RegExp(`^${escaped}$`).test(s);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Turn a SQL schema name into a safe TypeScript file stem.
|
|
32
|
+
* `__mj` stays `__mj`; `bsd_crm` stays `bsd_crm`; `Sales.Analytics` becomes `Sales_Analytics`.
|
|
33
|
+
*/
|
|
34
|
+
export function sanitizeSchemaFileName(schemaName) {
|
|
35
|
+
// Keep leading underscores — `__mj` is a real schema name and a valid file stem.
|
|
36
|
+
const cleaned = (schemaName ?? '').trim().replace(/[^A-Za-z0-9_]+/g, '_').replace(/_+$/g, '');
|
|
37
|
+
return cleaned.length > 0 ? cleaned : 'schema';
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Group entities by their raw `SchemaName` (trimmed, original casing of the
|
|
41
|
+
* first occurrence). Order inside each group is the input order.
|
|
42
|
+
*/
|
|
43
|
+
export function groupEntitiesBySchema(entities) {
|
|
44
|
+
const map = new Map();
|
|
45
|
+
for (const entity of entities) {
|
|
46
|
+
const key = (entity.SchemaName ?? '').trim() || 'unknown';
|
|
47
|
+
const list = map.get(key);
|
|
48
|
+
if (list) {
|
|
49
|
+
list.push(entity);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
map.set(key, [entity]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return map;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Map dirty entity names (from `newEntityList` ∪ `modifiedEntityList`) back to
|
|
59
|
+
* the schemas those entities live in. Comparison is case-insensitive.
|
|
60
|
+
*/
|
|
61
|
+
/**
|
|
62
|
+
* Resolve which schemas a file-emit pass should rebuild.
|
|
63
|
+
* `--skipdb` and `dirtySchemaOnly === false` rebuild every schema (write-if-changed
|
|
64
|
+
* still keeps mtimes stable). Otherwise only schemas that contain a new/modified
|
|
65
|
+
* entity are dirty — missing files are added later by {@link schemasToEmit}.
|
|
66
|
+
*/
|
|
67
|
+
export function resolveDirtySchemasForEmit(entities, dirtyEntityNames, skipDB, dirtySchemaOnly, deletedEntitySchemas = []) {
|
|
68
|
+
if (skipDB || !dirtySchemaOnly) {
|
|
69
|
+
return 'all';
|
|
70
|
+
}
|
|
71
|
+
const schemas = collectDirtySchemas(entities, dirtyEntityNames);
|
|
72
|
+
// Deletion arrives as a schema, not an entity name: by the time this runs the entity is gone
|
|
73
|
+
// from `entities`, so `collectDirtySchemas` could never resolve it. Union it in directly, or
|
|
74
|
+
// the schema is not rebuilt and the dead class survives on disk.
|
|
75
|
+
for (const schema of deletedEntitySchemas) {
|
|
76
|
+
const trimmed = (schema ?? '').trim();
|
|
77
|
+
if (trimmed.length > 0) {
|
|
78
|
+
schemas.add(trimmed);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return schemas;
|
|
82
|
+
}
|
|
83
|
+
export function collectDirtySchemas(entities, dirtyEntityNames) {
|
|
84
|
+
const dirty = new Set();
|
|
85
|
+
for (const name of dirtyEntityNames) {
|
|
86
|
+
const key = schemaKey(name);
|
|
87
|
+
if (key.length > 0) {
|
|
88
|
+
dirty.add(key);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const schemas = new Set();
|
|
92
|
+
if (dirty.size === 0) {
|
|
93
|
+
return schemas;
|
|
94
|
+
}
|
|
95
|
+
for (const entity of entities) {
|
|
96
|
+
if (dirty.has(schemaKey(entity.Name))) {
|
|
97
|
+
const schema = (entity.SchemaName ?? '').trim();
|
|
98
|
+
if (schema.length > 0) {
|
|
99
|
+
schemas.add(schema);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return schemas;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Decide which schema files to rebuild. Missing files are always rebuilt so a
|
|
107
|
+
* fresh clone / first run is complete. When `dirty` is `'all'` every schema is
|
|
108
|
+
* rebuilt (callers still use write-if-changed to keep mtimes stable).
|
|
109
|
+
*/
|
|
110
|
+
export function schemasToEmit(allSchemas, dirty, fileExists) {
|
|
111
|
+
if (dirty === 'all' || dirty == null) {
|
|
112
|
+
return [...allSchemas];
|
|
113
|
+
}
|
|
114
|
+
const dirtyLower = new Set([...dirty].map((s) => s.trim().toLowerCase()));
|
|
115
|
+
return allSchemas.filter((schema) => {
|
|
116
|
+
if (!fileExists(schema)) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return dirtyLower.has(schema.trim().toLowerCase());
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Bounded parallel map. Order of results matches `items`. A limit of 1 is serial.
|
|
124
|
+
*/
|
|
125
|
+
export async function mapLimit(items, limit, mapper) {
|
|
126
|
+
const concurrency = Math.max(1, Math.floor(limit));
|
|
127
|
+
const results = new Array(items.length);
|
|
128
|
+
let next = 0;
|
|
129
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
130
|
+
while (next < items.length) {
|
|
131
|
+
const index = next;
|
|
132
|
+
next += 1;
|
|
133
|
+
results[index] = await mapper(items[index], index);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
await Promise.all(workers);
|
|
137
|
+
return results;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Barrel that re-exports every per-schema file. Uses `.js` specifiers so the
|
|
141
|
+
* file is valid in `"type": "module"` packages (MJCoreEntities, MJServer).
|
|
142
|
+
*/
|
|
143
|
+
export function buildSchemaBarrel(schemas, relativeDir, extraHeader) {
|
|
144
|
+
const dir = relativeDir.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
145
|
+
const exports = [...schemas]
|
|
146
|
+
.sort((a, b) => a.localeCompare(b))
|
|
147
|
+
.map((schema) => `export * from './${dir}/${sanitizeSchemaFileName(schema)}.js';`)
|
|
148
|
+
.join('\n');
|
|
149
|
+
return `${extraHeader}${exports}\n`;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Names of files in a per-schema emit directory that no live schema claims.
|
|
153
|
+
*
|
|
154
|
+
* Kept pure and separate from the deletion so the decision is testable without a
|
|
155
|
+
* filesystem. `fileNames` is a plain directory listing; `schemas` is every schema in the
|
|
156
|
+
* run, not just the dirty subset.
|
|
157
|
+
*/
|
|
158
|
+
export function selectOrphanedSchemaFiles(fileNames, schemas) {
|
|
159
|
+
const live = new Set(schemas.map((schema) => `${sanitizeSchemaFileName(schema)}.ts`));
|
|
160
|
+
return fileNames.filter((name) => name.endsWith('.ts') && !live.has(name)).sort();
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Delete per-schema files that no live schema claims, returning what was removed.
|
|
164
|
+
*
|
|
165
|
+
* A schema that empties out — its last table dropped — leaves its file on disk. The barrel
|
|
166
|
+
* stops exporting it, which is not enough: `tsc` still compiles it through `include`, the
|
|
167
|
+
* class-registration manifest still globs it into the registry, and the CodeGen-tail guard
|
|
168
|
+
* reads every `.ts` in the directory regardless of what the barrel names. So a deleted
|
|
169
|
+
* entity keeps a live `@RegisterClass` registration and inflates the roster the guard
|
|
170
|
+
* compares. The old monolith could not drift this way; it was rewritten in full every run.
|
|
171
|
+
*
|
|
172
|
+
* Safe under dirty-schema regen: `schemas` is the full set for the run, so a schema that
|
|
173
|
+
* simply was not dirty this time is still live and is never pruned.
|
|
174
|
+
*/
|
|
175
|
+
export function pruneOrphanedSchemaFiles(directory, schemas) {
|
|
176
|
+
if (!fs.existsSync(directory)) {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
const orphans = selectOrphanedSchemaFiles(fs.readdirSync(directory), schemas);
|
|
180
|
+
for (const name of orphans) {
|
|
181
|
+
fs.unlinkSync(path.join(directory, name));
|
|
182
|
+
}
|
|
183
|
+
return orphans;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Fill in a partial {@link SchemaEmitOptions} from config defaults.
|
|
187
|
+
*
|
|
188
|
+
* Shared by both generators so the defaults cannot drift between entity subclasses and
|
|
189
|
+
* GraphQL resolvers — they must agree, or one emits per-schema while the other emits a
|
|
190
|
+
* monolith and the two rosters stop lining up.
|
|
191
|
+
*
|
|
192
|
+
* `parallel` / `concurrency` are resolved for every caller even though only the entity
|
|
193
|
+
* generator uses them: its per-schema assembly is async (each file awaits its entities),
|
|
194
|
+
* while GraphQL assembly is synchronous, so there is nothing to overlap there.
|
|
195
|
+
*/
|
|
196
|
+
export function resolveSchemaEmitOptions(options, defaults) {
|
|
197
|
+
return {
|
|
198
|
+
perSchema: options?.perSchema ?? defaults?.perSchema ?? true,
|
|
199
|
+
dirtySchemas: options?.dirtySchemas ?? 'all',
|
|
200
|
+
parallel: options?.parallel ?? defaults?.parallel ?? true,
|
|
201
|
+
concurrency: options?.concurrency ?? defaults?.concurrency ?? 8,
|
|
202
|
+
writeIfChanged: options?.writeIfChanged ?? defaults?.writeIfChanged ?? true,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Write one emitted file, honouring the write-if-changed setting.
|
|
207
|
+
*
|
|
208
|
+
* Shared for the same reason as {@link resolveSchemaEmitOptions}: both generators must treat
|
|
209
|
+
* an unchanged file identically, or one leaves mtimes alone while the other churns them and
|
|
210
|
+
* downstream incremental builds see phantom work.
|
|
211
|
+
*/
|
|
212
|
+
export function emitSchemaFile(filePath, content, useWriteIfChanged) {
|
|
213
|
+
if (useWriteIfChanged) {
|
|
214
|
+
writeFileIfChanged(filePath, content);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
218
|
+
fs.writeFileSync(filePath, content);
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=schema-emit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-emit.js","sourceRoot":"","sources":["../../src/Misc/schema-emit.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -48,6 +48,12 @@ export * from './Database/dbSchema.js';
|
|
|
48
48
|
export * from './Database/manage-metadata.js';
|
|
49
49
|
export * from './Database/sql_codegen.js';
|
|
50
50
|
export * from './Database/sql.js';
|
|
51
|
+
export * from './Database/schema-scope.js';
|
|
52
|
+
export * from './Database/exclude-tables.js';
|
|
53
|
+
export * from './Config/schema-output.js';
|
|
54
|
+
export * from './Misc/file-write.js';
|
|
55
|
+
export * from './Misc/emit-stats.js';
|
|
56
|
+
export * from './Misc/schema-emit.js';
|
|
51
57
|
export * from './Misc/entity_subclasses_codegen.js';
|
|
52
58
|
export * from './Misc/action_subclasses_codegen.js';
|
|
53
59
|
export * from './Misc/remote_operations_codegen.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AAGtC,cAAc,oCAAoC,CAAA;AAClD,cAAc,yDAAyD,CAAA;AACvE,cAAc,2DAA2D,CAAA;AACzE,cAAc,2DAA2D,CAAA;AACzE,cAAc,6DAA6D,CAAA;AAC3E,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AAGtC,cAAc,oCAAoC,CAAA;AAClD,cAAc,yDAAyD,CAAA;AACvE,cAAc,2DAA2D,CAAA;AACzE,cAAc,2DAA2D,CAAA;AACzE,cAAc,6DAA6D,CAAA;AAC3E,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,yBAAyB,CAAA;AACvC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA;AACtC,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAGlC,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAA;AAG7C,cAAc,2BAA2B,CAAA;AACzC,cAAc,qCAAqC,CAAC;AACpD,cAAc,qDAAqD,CAAC;AACpE,cAAc,8CAA8C,CAAC;AAC7D,cAAc,6CAA6C,CAAC;AAG5D,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,yBAAyB,CAAA;AAGvC,cAAc,+CAA+C,CAAA;AAG7D,cAAc,uCAAuC,CAAA;AACrD,cAAc,yCAAyC,CAAA;AACvD,cAAc,2CAA2C,CAAA;AAGzD,cAAc,cAAc,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,12 @@ export * from './Database/dbSchema.js';
|
|
|
50
50
|
export * from './Database/manage-metadata.js';
|
|
51
51
|
export * from './Database/sql_codegen.js';
|
|
52
52
|
export * from './Database/sql.js';
|
|
53
|
+
export * from './Database/schema-scope.js';
|
|
54
|
+
export * from './Database/exclude-tables.js';
|
|
55
|
+
export * from './Config/schema-output.js';
|
|
56
|
+
export * from './Misc/file-write.js';
|
|
57
|
+
export * from './Misc/emit-stats.js';
|
|
58
|
+
export * from './Misc/schema-emit.js';
|
|
53
59
|
// Code generation exports
|
|
54
60
|
export * from './Misc/entity_subclasses_codegen.js';
|
|
55
61
|
export * from './Misc/action_subclasses_codegen.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,wBAAwB;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AAEtC,mBAAmB;AACnB,cAAc,oCAAoC,CAAA;AAClD,cAAc,yDAAyD,CAAA;AACvE,cAAc,2DAA2D,CAAA;AACzE,cAAc,2DAA2D,CAAA;AACzE,cAAc,6DAA6D,CAAA;AAC3E,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,wBAAwB;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AAEtC,mBAAmB;AACnB,cAAc,oCAAoC,CAAA;AAClD,cAAc,yDAAyD,CAAA;AACvE,cAAc,2DAA2D,CAAA;AACzE,cAAc,2DAA2D,CAAA;AACzE,cAAc,6DAA6D,CAAA;AAC3E,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,yBAAyB,CAAA;AACvC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA;AACtC,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAElC,0BAA0B;AAC1B,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAA;AAE7C,kBAAkB;AAClB,cAAc,2BAA2B,CAAA;AACzC,cAAc,qCAAqC,CAAC;AACpD,cAAc,qDAAqD,CAAC;AACpE,cAAc,8CAA8C,CAAC;AAC7D,cAAc,6CAA6C,CAAC;AAE5D,kBAAkB;AAClB,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,yBAAyB,CAAA;AAEvC,sBAAsB;AACtB,cAAc,+CAA+C,CAAA;AAE7D,uBAAuB;AACvB,cAAc,uCAAuC,CAAA;AACrD,cAAc,yCAAyC,CAAA;AACvD,cAAc,2CAA2C,CAAA;AAEzD,cAAc;AACd,cAAc,cAAc,CAAA"}
|
package/dist/runCodeGen.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Supports both SQL Server and PostgreSQL database platforms via the dbPlatform configuration.
|
|
8
8
|
*/
|
|
9
9
|
import { CommandInfo } from './Config/config.js';
|
|
10
|
+
import { SchemaEmitOptions } from './Misc/schema-emit.js';
|
|
10
11
|
import * as MJ from '@memberjunction/core';
|
|
11
12
|
import { CommandExecutionResult } from './Misc/runCommand.js';
|
|
12
13
|
import { CodeGenConnection, DataSourceResult as ProviderDataSourceResult } from './Database/codeGenDatabaseProvider.js';
|
|
@@ -27,8 +28,17 @@ export declare class RunCodeGenBase {
|
|
|
27
28
|
context: string;
|
|
28
29
|
message: string;
|
|
29
30
|
}>;
|
|
30
|
-
/**
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Record any failed entries from a BEFORE/AFTER command batch, paired by index.
|
|
33
|
+
*
|
|
34
|
+
* Returns just this batch's failures. Callers log what they get back rather than walking
|
|
35
|
+
* `commandFailures`, which accumulates across phases — iterating the whole array in the
|
|
36
|
+
* AFTER handler re-logged every BEFORE failure a second time.
|
|
37
|
+
*/
|
|
38
|
+
protected recordCommandFailures(phase: string, cmds: CommandInfo[], results: CommandExecutionResult[]): Array<{
|
|
39
|
+
context: string;
|
|
40
|
+
message: string;
|
|
41
|
+
}>;
|
|
32
42
|
/**
|
|
33
43
|
* Acquire the per-platform data source for a CodeGen run.
|
|
34
44
|
*
|
|
@@ -65,6 +75,14 @@ export declare class RunCodeGenBase {
|
|
|
65
75
|
* Accepts an existing data source — does NOT create connections or call process.exit().
|
|
66
76
|
*/
|
|
67
77
|
protected executeCodeGenPipeline(dataSource: DataSourceResult, skipDatabaseGeneration?: boolean, skipFileGeneration?: boolean): Promise<boolean>;
|
|
78
|
+
/**
|
|
79
|
+
* Resolve dirty-schema emit options for one generator call.
|
|
80
|
+
* `--skipdb` (files only) and `fileEmit.dirtySchemaOnly === false` rebuild
|
|
81
|
+
* every schema; write-if-changed still keeps mtimes stable. A full run with
|
|
82
|
+
* no new/modified entities yields an empty Set — the generator then only
|
|
83
|
+
* writes schema files that are missing.
|
|
84
|
+
*/
|
|
85
|
+
protected buildSchemaEmitOptions(entities: MJ.EntityInfo[], skipDB: boolean): SchemaEmitOptions;
|
|
68
86
|
/**
|
|
69
87
|
* Runs the file-generation phase of CodeGen: GraphQL resolvers, entity
|
|
70
88
|
* subclasses (core + non-core), Angular components, DB schema JSON, and
|
package/dist/runCodeGen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runCodeGen.d.ts","sourceRoot":"","sources":["../src/runCodeGen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"runCodeGen.d.ts","sourceRoot":"","sources":["../src/runCodeGen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,EAA4G,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACxJ,OAAO,EAAyC,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI9F,OAAO,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC3C,OAAO,EAAmB,sBAAsB,EAA8B,MAAM,mBAAmB,CAAC;AASxG,OAAO,EAAE,iBAAiB,EAA2B,gBAAgB,IAAI,wBAAwB,EAAkC,MAAM,oCAAoC,CAAC;AAI9K,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAG5D,OAAO,8DAA8D,CAAC;AAStE,KAAK,gBAAgB,GAAG,wBAAwB,CAAC;AAEjD;;GAEG;AACH,qBAAa,cAAc;IACzB;;;;OAIG;IACH,SAAS,CAAC,eAAe,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IAE5E;;;;;;OAMG;IACH,SAAS,CAAC,qBAAqB,CAC7B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,WAAW,EAAE,EACnB,OAAO,EAAE,sBAAsB,EAAE,GAChC,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAkB9C;;;;;;;;;;;;;OAaG;IACU,eAAe,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAIzD;;OAEG;IACH;;;;OAIG;IACU,YAAY,CAAC,UAAU,EAAE,gBAAgB,EAAE,sBAAsB,GAAE,OAAe,EAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,kBAAkB,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBrK,GAAG,CAAC,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe;IAQ7F;;;;OAIG;IACU,aAAa,CAAC,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IAiD9H;;;OAGG;cACa,sBAAsB,CAAC,UAAU,EAAE,gBAAgB,EAAE,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAyVpK;;;;;;OAMG;IACH,SAAS,CAAC,sBAAsB,CAAC,QAAQ,EAAE,EAAE,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG,iBAAiB;IAa/F;;;;;;;;OAQG;cACa,sBAAsB,CACpC,IAAI,EAAE,iBAAiB,EACvB,WAAW,EAAE,QAAQ,EACrB,EAAE,EAAE,EAAE,CAAC,QAAQ,EACf,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,OAAO,CAAC;CA+QpB;AAED;;GAEG;AACH,wBAAsB,+BAA+B,CAAC,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe,iBAGjI;AAED;;;;;GAKG;AACH,wBAAsB,yCAAyC,CAC7D,sBAAsB,GAAE,OAAe,EACvC,kBAAkB,GAAE,OAAe,GAClC,OAAO,CAAC,WAAW,CAAC,CAGtB"}
|
package/dist/runCodeGen.js
CHANGED
|
@@ -11,11 +11,14 @@ import { SQLCodeGenBase } from './Database/sql_codegen.js';
|
|
|
11
11
|
import { EntitySubClassGeneratorBase } from './Misc/entity_subclasses_codegen.js';
|
|
12
12
|
import { ManageMetadataBase } from './Database/manage-metadata.js';
|
|
13
13
|
import { applyIncludeSchemaScope } from './Database/schema-scope.js';
|
|
14
|
+
import { partitionEntitiesByOutputDirectory } from './Config/schema-output.js';
|
|
14
15
|
import { outputDir, commands, configInfo, getSettingValue, dbPlatform, getExternalEntitySchemas, initializeConfig } from './Config/config.js';
|
|
16
|
+
import { resolveDirtySchemasForEmit, schemaKey } from './Misc/schema-emit.js';
|
|
17
|
+
import { EmitStats } from './Misc/emit-stats.js';
|
|
15
18
|
import { logError, logStatus, logWarning, startSpinner, updateSpinner, succeedSpinner, failSpinner, warnSpinner } from './Misc/status_logging.js';
|
|
16
19
|
import { CodeGenReporter } from './Misc/codegen-reporter.js';
|
|
17
20
|
import * as MJ from '@memberjunction/core';
|
|
18
|
-
import { RunCommandsBase } from './Misc/runCommand.js';
|
|
21
|
+
import { RunCommandsBase, formatCommandFailureDetail } from './Misc/runCommand.js';
|
|
19
22
|
import { DBSchemaGeneratorBase } from './Database/dbSchema.js';
|
|
20
23
|
import { AngularClientGeneratorBase } from './Angular/angular-codegen.js';
|
|
21
24
|
import { CreateNewUserBase } from './Misc/createNewUser.js';
|
|
@@ -43,19 +46,29 @@ export class RunCodeGenBase {
|
|
|
43
46
|
*/
|
|
44
47
|
this.commandFailures = [];
|
|
45
48
|
}
|
|
46
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Record any failed entries from a BEFORE/AFTER command batch, paired by index.
|
|
51
|
+
*
|
|
52
|
+
* Returns just this batch's failures. Callers log what they get back rather than walking
|
|
53
|
+
* `commandFailures`, which accumulates across phases — iterating the whole array in the
|
|
54
|
+
* AFTER handler re-logged every BEFORE failure a second time.
|
|
55
|
+
*/
|
|
47
56
|
recordCommandFailures(phase, cmds, results) {
|
|
57
|
+
const recorded = [];
|
|
48
58
|
results.forEach((r, i) => {
|
|
49
59
|
if (!r.success) {
|
|
50
60
|
const cmd = cmds[i];
|
|
51
61
|
const cmdText = cmd ? [cmd.command, ...(cmd.args ?? [])].join(' ').trim() : `command #${i + 1}`;
|
|
52
|
-
const detail = (r
|
|
53
|
-
|
|
62
|
+
const detail = formatCommandFailureDetail(r);
|
|
63
|
+
const failure = {
|
|
54
64
|
context: `${phase} command`,
|
|
55
65
|
message: detail ? `\`${cmdText}\` failed: ${detail}` : `\`${cmdText}\` failed`,
|
|
56
|
-
}
|
|
66
|
+
};
|
|
67
|
+
this.commandFailures.push(failure);
|
|
68
|
+
recorded.push(failure);
|
|
57
69
|
}
|
|
58
70
|
});
|
|
71
|
+
return recorded;
|
|
59
72
|
}
|
|
60
73
|
/**
|
|
61
74
|
* Acquire the per-platform data source for a CodeGen run.
|
|
@@ -149,8 +162,9 @@ export class RunCodeGenBase {
|
|
|
149
162
|
};
|
|
150
163
|
}
|
|
151
164
|
catch (e) {
|
|
165
|
+
const message = e instanceof Error ? (e.stack ?? e.message) : String(e);
|
|
152
166
|
failSpinner('CodeGen failed: ' + e);
|
|
153
|
-
logError(
|
|
167
|
+
logError(message);
|
|
154
168
|
return {
|
|
155
169
|
success: false,
|
|
156
170
|
command: 'codegen',
|
|
@@ -205,7 +219,12 @@ export class RunCodeGenBase {
|
|
|
205
219
|
const results = await runCommandsObject.runCommands(beforeCommands);
|
|
206
220
|
if (results.some((r) => !r.success)) {
|
|
207
221
|
logError('ERROR running one or more BEFORE commands');
|
|
208
|
-
this.recordCommandFailures('BEFORE', beforeCommands, results);
|
|
222
|
+
const recorded = this.recordCommandFailures('BEFORE', beforeCommands, results);
|
|
223
|
+
pipelineSuccess = false;
|
|
224
|
+
for (const failure of recorded) {
|
|
225
|
+
logError(failure.message);
|
|
226
|
+
reporter.note(failure.message);
|
|
227
|
+
}
|
|
209
228
|
}
|
|
210
229
|
}
|
|
211
230
|
updateSpinner('Executing before-all SQL Scripts...');
|
|
@@ -355,6 +374,57 @@ export class RunCodeGenBase {
|
|
|
355
374
|
return false;
|
|
356
375
|
}
|
|
357
376
|
}
|
|
377
|
+
// Metadata/view coherence. The CRUD validator asks "can the runtime WRITE this
|
|
378
|
+
// entity"; this asks the prior question — "can it READ it at all".
|
|
379
|
+
//
|
|
380
|
+
// A field the metadata declares but the base view does not produce makes every
|
|
381
|
+
// read of that entity fail with `column "X" does not exist`. Grids render that
|
|
382
|
+
// as an empty result rather than an error, so the entity looks like it holds no
|
|
383
|
+
// data. Nothing else in the pipeline notices, and the condition is permanent for
|
|
384
|
+
// any schema in `excludeSchemas` (CodeGen emits permissions only for those, so
|
|
385
|
+
// there is no regeneration pass to repair the view) — which by convention
|
|
386
|
+
// includes `__mj` on essentially every install.
|
|
387
|
+
//
|
|
388
|
+
// Reported, not fatal, by default: existing installs may already carry this drift
|
|
389
|
+
// and failing their next codegen run would be worse than telling them about it.
|
|
390
|
+
// Set MJ_CODEGEN_STRICT_FIELD_RESOLUTION=true to make it a hard gate; that is the
|
|
391
|
+
// intended default once fleets are clean.
|
|
392
|
+
try {
|
|
393
|
+
startSpinner('Validating entity fields resolve against their base views...');
|
|
394
|
+
const baseline = md.Entities.filter(e => e.IncludeInAPI);
|
|
395
|
+
const gaps = await sqlCodeGenObject.DBProvider.validateEntityFieldsResolve(conn, baseline);
|
|
396
|
+
if (gaps.length > 0) {
|
|
397
|
+
const affected = new Set(gaps.map(g => `${g.schema}.${g.entity}`));
|
|
398
|
+
const list = gaps
|
|
399
|
+
.map(g => ` - [${g.schema}] ${g.entity} → ${g.baseView} does not produce "${g.field}"${g.isVirtual ? ' (virtual)' : ''}`)
|
|
400
|
+
.join('\n');
|
|
401
|
+
const strict = process.env.MJ_CODEGEN_STRICT_FIELD_RESOLUTION === 'true';
|
|
402
|
+
const summary = `${gaps.length} declared field(s) across ${affected.size} entity(ies) are not produced by their base view.\n` +
|
|
403
|
+
`Every read of these entities fails with 'column ... does not exist'. A grid shows that as\n` +
|
|
404
|
+
`"no data" rather than an error, so the entity appears empty while its table is full.\n` +
|
|
405
|
+
`${list}\n\n` +
|
|
406
|
+
`Cause: a migration added the column to the base TABLE and registered the EntityField, but did\n` +
|
|
407
|
+
`not rebuild the base VIEW. For schemas listed in excludeSchemas (typically __mj) CodeGen never\n` +
|
|
408
|
+
`regenerates the view, so this does not self-heal.\n` +
|
|
409
|
+
`Fix: rebuild the affected base view(s). PostgreSQL permits appending columns in place via\n` +
|
|
410
|
+
`CREATE OR REPLACE VIEW, so no DROP/CASCADE is required.`;
|
|
411
|
+
if (strict) {
|
|
412
|
+
failSpinner(`Field-resolution validation FAILED: ${gaps.length} unreadable field(s)`);
|
|
413
|
+
logError(summary);
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
warnSpinner(`Field-resolution validation found ${gaps.length} unreadable field(s)`);
|
|
417
|
+
logWarning(summary);
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
succeedSpinner('Entity fields all resolve against their base views');
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
catch (e) {
|
|
424
|
+
// Never fail the run on the validator's own error — it is a reporter.
|
|
425
|
+
warnSpinner('Field-resolution validation could not run');
|
|
426
|
+
logWarning(`Field-resolution validator threw: ${e instanceof Error ? e.message : String(e)}`);
|
|
427
|
+
}
|
|
358
428
|
// Surface upstream SQL-pipeline failure even if validator passed: a
|
|
359
429
|
// green validator just means whatever DID get generated is consistent;
|
|
360
430
|
// it doesn't whitewash an upstream batch error.
|
|
@@ -394,7 +464,12 @@ export class RunCodeGenBase {
|
|
|
394
464
|
const results = await runCommandsObject.runCommands(afterCommands);
|
|
395
465
|
if (results.some((r) => !r.success)) {
|
|
396
466
|
failSpinner('ERROR running one or more AFTER commands');
|
|
397
|
-
this.recordCommandFailures('AFTER', afterCommands, results);
|
|
467
|
+
const recorded = this.recordCommandFailures('AFTER', afterCommands, results);
|
|
468
|
+
pipelineSuccess = false;
|
|
469
|
+
for (const failure of recorded) {
|
|
470
|
+
logError(failure.message);
|
|
471
|
+
reporter.note(failure.message);
|
|
472
|
+
}
|
|
398
473
|
}
|
|
399
474
|
else
|
|
400
475
|
succeedSpinner('AFTER commands completed');
|
|
@@ -415,7 +490,8 @@ export class RunCodeGenBase {
|
|
|
415
490
|
logStatus('MJ CodeGen Complete! ' + md.Entities.length + ' entities processed in ' + totalSeconds + 's @ ' + endTime.toLocaleString());
|
|
416
491
|
// A BEFORE/AFTER command failure fails the run (success=false, exit 1) so it
|
|
417
492
|
// isn't silently swallowed — details flow into the structured result below.
|
|
418
|
-
|
|
493
|
+
pipelineSuccess = pipelineSuccess && this.commandFailures.length === 0;
|
|
494
|
+
return pipelineSuccess;
|
|
419
495
|
}
|
|
420
496
|
catch (err) {
|
|
421
497
|
pipelineSuccess = false;
|
|
@@ -439,6 +515,19 @@ export class RunCodeGenBase {
|
|
|
439
515
|
logStatus('CodeGen report written: ' + filePath);
|
|
440
516
|
}
|
|
441
517
|
}
|
|
518
|
+
/**
|
|
519
|
+
* Resolve dirty-schema emit options for one generator call.
|
|
520
|
+
* `--skipdb` (files only) and `fileEmit.dirtySchemaOnly === false` rebuild
|
|
521
|
+
* every schema; write-if-changed still keeps mtimes stable. A full run with
|
|
522
|
+
* no new/modified entities yields an empty Set — the generator then only
|
|
523
|
+
* writes schema files that are missing.
|
|
524
|
+
*/
|
|
525
|
+
buildSchemaEmitOptions(entities, skipDB) {
|
|
526
|
+
const fileEmit = configInfo.fileEmit;
|
|
527
|
+
return {
|
|
528
|
+
dirtySchemas: resolveDirtySchemasForEmit(entities, [...ManageMetadataBase.newEntityList, ...ManageMetadataBase.modifiedEntityList], skipDB, fileEmit?.dirtySchemaOnly !== false, ManageMetadataBase.deletedEntitySchemaList),
|
|
529
|
+
};
|
|
530
|
+
}
|
|
442
531
|
/**
|
|
443
532
|
* Runs the file-generation phase of CodeGen: GraphQL resolvers, entity
|
|
444
533
|
* subclasses (core + non-core), Angular components, DB schema JSON, and
|
|
@@ -450,15 +539,16 @@ export class RunCodeGenBase {
|
|
|
450
539
|
*/
|
|
451
540
|
async runFileGenerationPhase(conn, currentUser, md, skipDB) {
|
|
452
541
|
const reporter = CodeGenReporter.Instance;
|
|
542
|
+
EmitStats.Reset();
|
|
453
543
|
const apiEntities = md.Entities.filter((e) => e.IncludeInAPI);
|
|
454
|
-
const excludedSchemaNames = configInfo.excludeSchemas.map(s => s
|
|
455
|
-
const includedEntities = apiEntities.filter((e) => !excludedSchemaNames.includes(e.SchemaName
|
|
544
|
+
const excludedSchemaNames = configInfo.excludeSchemas.map(s => schemaKey(s));
|
|
545
|
+
const includedEntities = apiEntities.filter((e) => !excludedSchemaNames.includes(schemaKey(e.SchemaName)));
|
|
456
546
|
const excludedCount = apiEntities.length - includedEntities.length;
|
|
457
547
|
if (excludedCount > 0) {
|
|
458
548
|
const excludedBySchema = apiEntities
|
|
459
|
-
.filter((e) => excludedSchemaNames.includes(e.SchemaName
|
|
549
|
+
.filter((e) => excludedSchemaNames.includes(schemaKey(e.SchemaName)))
|
|
460
550
|
.reduce((acc, e) => {
|
|
461
|
-
const schema = e.SchemaName.trim();
|
|
551
|
+
const schema = (e.SchemaName ?? '').trim() || '(none)';
|
|
462
552
|
acc[schema] = (acc[schema] || 0) + 1;
|
|
463
553
|
return acc;
|
|
464
554
|
}, {});
|
|
@@ -467,8 +557,8 @@ export class RunCodeGenBase {
|
|
|
467
557
|
.join(', ');
|
|
468
558
|
logStatus('Excluded ' + excludedCount + ' entities from code generation by schema: ' + schemaDetails);
|
|
469
559
|
}
|
|
470
|
-
const coreEntities = includedEntities.filter((e) => e.SchemaName
|
|
471
|
-
const nonCoreEntities = includedEntities.filter((e) => e.SchemaName
|
|
560
|
+
const coreEntities = includedEntities.filter((e) => schemaKey(e.SchemaName) === schemaKey(mjCoreSchema));
|
|
561
|
+
const nonCoreEntities = includedEntities.filter((e) => schemaKey(e.SchemaName) !== schemaKey(mjCoreSchema));
|
|
472
562
|
// Entities whose schemas are owned by OTHER packages (see entityPackageName map). They must be
|
|
473
563
|
// excluded from every artifact this package emits — GraphQL ObjectTypes included. If two packages
|
|
474
564
|
// both emit an ObjectType for the same entity, graphql-js rejects the unified schema at boot with
|
|
@@ -476,7 +566,7 @@ export class RunCodeGenBase {
|
|
|
476
566
|
// crash-loops.
|
|
477
567
|
const externalSchemas = getExternalEntitySchemas().map(s => s.toLowerCase());
|
|
478
568
|
const localNonCoreEntities = externalSchemas.length > 0
|
|
479
|
-
? nonCoreEntities.filter(e => !externalSchemas.includes(e.SchemaName
|
|
569
|
+
? nonCoreEntities.filter(e => !externalSchemas.includes(schemaKey(e.SchemaName)))
|
|
480
570
|
: nonCoreEntities;
|
|
481
571
|
const isVerbose = configInfo?.verboseOutput ?? false;
|
|
482
572
|
if (!isVerbose)
|
|
@@ -486,7 +576,7 @@ export class RunCodeGenBase {
|
|
|
486
576
|
if (isVerbose)
|
|
487
577
|
startSpinner('Generating CORE Entity GraphQL Resolver Code...');
|
|
488
578
|
const graphQLGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(GraphQLServerGeneratorBase);
|
|
489
|
-
const ok = await reporter.phase('generateGraphQLCore', async () => graphQLGenerator.generateGraphQLServerCode(coreEntities, graphQLCoreResolversOutputDir, '@memberjunction/core-entities', true));
|
|
579
|
+
const ok = await reporter.phase('generateGraphQLCore', async () => graphQLGenerator.generateGraphQLServerCode(coreEntities, graphQLCoreResolversOutputDir, '@memberjunction/core-entities', true, this.buildSchemaEmitOptions(coreEntities, skipDB)));
|
|
490
580
|
if (!ok) {
|
|
491
581
|
failSpinner('Error generating GraphQL server code');
|
|
492
582
|
return false;
|
|
@@ -502,7 +592,16 @@ export class RunCodeGenBase {
|
|
|
502
592
|
const entityPackageName = typeof configInfo.entityPackageName === 'string'
|
|
503
593
|
? (configInfo.entityPackageName || 'mj_generatedentities')
|
|
504
594
|
: 'mj_generatedentities';
|
|
505
|
-
const
|
|
595
|
+
const graphqlGroups = partitionEntitiesByOutputDirectory(localNonCoreEntities, 'GraphQLServer', graphqlOutputDir, configInfo.schemaOutput);
|
|
596
|
+
const ok = await reporter.phase('generateGraphQL', async () => {
|
|
597
|
+
for (const [dir, group] of graphqlGroups) {
|
|
598
|
+
const groupOk = graphQLGenerator.generateGraphQLServerCode(group, dir, entityPackageName, false, this.buildSchemaEmitOptions(group, skipDB));
|
|
599
|
+
if (!groupOk) {
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return true;
|
|
604
|
+
});
|
|
506
605
|
if (!ok) {
|
|
507
606
|
failSpinner('Error generating GraphQL Resolver code');
|
|
508
607
|
return false;
|
|
@@ -517,7 +616,7 @@ export class RunCodeGenBase {
|
|
|
517
616
|
if (isVerbose)
|
|
518
617
|
startSpinner('Generating CORE Entity Subclass Code...');
|
|
519
618
|
const entitySubClassGeneratorObject = MJGlobal.Instance.ClassFactory.CreateInstance(EntitySubClassGeneratorBase);
|
|
520
|
-
const ok = await reporter.phase('generateEntitySubclassesCore', () => entitySubClassGeneratorObject.generateAllEntitySubClasses(conn, coreEntities, coreEntitySubClassOutputDir, skipDB));
|
|
619
|
+
const ok = await reporter.phase('generateEntitySubclassesCore', () => entitySubClassGeneratorObject.generateAllEntitySubClasses(conn, coreEntities, coreEntitySubClassOutputDir, skipDB, this.buildSchemaEmitOptions(coreEntities, skipDB)));
|
|
521
620
|
if (!ok) {
|
|
522
621
|
failSpinner('Error generating entity subclass code');
|
|
523
622
|
return false;
|
|
@@ -530,7 +629,16 @@ export class RunCodeGenBase {
|
|
|
530
629
|
if (isVerbose)
|
|
531
630
|
startSpinner('Generating Entity Subclass Code...');
|
|
532
631
|
const entitySubClassGeneratorObject = MJGlobal.Instance.ClassFactory.CreateInstance(EntitySubClassGeneratorBase);
|
|
533
|
-
const
|
|
632
|
+
const entityGroups = partitionEntitiesByOutputDirectory(localNonCoreEntities, 'EntitySubClasses', entitySubClassOutputDir, configInfo.schemaOutput);
|
|
633
|
+
const ok = await reporter.phase('generateEntitySubclasses', async () => {
|
|
634
|
+
for (const [dir, group] of entityGroups) {
|
|
635
|
+
const groupOk = await entitySubClassGeneratorObject.generateAllEntitySubClasses(conn, group, dir, skipDB, this.buildSchemaEmitOptions(group, skipDB));
|
|
636
|
+
if (!groupOk) {
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
return true;
|
|
641
|
+
});
|
|
534
642
|
if (!ok) {
|
|
535
643
|
failSpinner('Error generating entity subclass code');
|
|
536
644
|
return false;
|
|
@@ -558,7 +666,16 @@ export class RunCodeGenBase {
|
|
|
558
666
|
if (isVerbose)
|
|
559
667
|
startSpinner('Generating Angular Code...');
|
|
560
668
|
const angularGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(AngularClientGeneratorBase);
|
|
561
|
-
const
|
|
669
|
+
const angularGroups = partitionEntitiesByOutputDirectory(localNonCoreEntities, 'Angular', angularOutputDir, configInfo.schemaOutput);
|
|
670
|
+
const ok = await reporter.phase('generateAngular', async () => {
|
|
671
|
+
for (const [dir, group] of angularGroups) {
|
|
672
|
+
const groupOk = angularGenerator.generateAngularCode(group, dir, '', currentUser);
|
|
673
|
+
if (!groupOk) {
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return true;
|
|
678
|
+
});
|
|
562
679
|
if (!ok) {
|
|
563
680
|
failSpinner('Error generating Angular code');
|
|
564
681
|
return false;
|
|
@@ -644,6 +761,15 @@ export class RunCodeGenBase {
|
|
|
644
761
|
}
|
|
645
762
|
else if (isVerbose)
|
|
646
763
|
warnSpinner('Remote Operations output directory NOT found in config file, skipping...');
|
|
764
|
+
const emit = EmitStats.Snapshot();
|
|
765
|
+
reporter.counter('filesWritten', emit.filesWritten);
|
|
766
|
+
reporter.counter('filesSkipped', emit.filesSkipped);
|
|
767
|
+
reporter.counter('schemasEmitted', emit.schemasEmitted);
|
|
768
|
+
reporter.counter('schemasSkipped', emit.schemasSkipped);
|
|
769
|
+
reporter.mark('emitStats', emit);
|
|
770
|
+
logStatus(`File emit: wrote ${emit.filesWritten}, skipped ${emit.filesSkipped}, ` +
|
|
771
|
+
`schemas emitted ${emit.schemasEmitted} / skipped ${emit.schemasSkipped} ` +
|
|
772
|
+
`(assemble ${emit.assembleMs}ms)`);
|
|
647
773
|
SQLLogging.finishSQLLogging();
|
|
648
774
|
if (!isVerbose)
|
|
649
775
|
succeedSpinner('TypeScript code generation completed');
|