@memberjunction/codegen-lib 3.3.0 → 4.0.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 +56 -1
- package/dist/Angular/angular-codegen.d.ts +1 -1
- package/dist/Angular/angular-codegen.d.ts.map +1 -1
- package/dist/Angular/angular-codegen.js +80 -136
- package/dist/Angular/angular-codegen.js.map +1 -1
- package/dist/Angular/entity-data-grid-related-entity-component.d.ts +1 -1
- package/dist/Angular/entity-data-grid-related-entity-component.js +6 -9
- package/dist/Angular/entity-data-grid-related-entity-component.js.map +1 -1
- package/dist/Angular/join-grid-related-entity-component.d.ts +1 -1
- package/dist/Angular/join-grid-related-entity-component.js +8 -36
- package/dist/Angular/join-grid-related-entity-component.js.map +1 -1
- package/dist/Angular/related-entity-components.js +13 -79
- package/dist/Angular/related-entity-components.js.map +1 -1
- package/dist/Angular/timeline-related-entity-component.d.ts +1 -1
- package/dist/Angular/timeline-related-entity-component.js +14 -38
- package/dist/Angular/timeline-related-entity-component.js.map +1 -1
- package/dist/Angular/user-view-grid-related-entity-component.d.ts +43 -0
- package/dist/Angular/user-view-grid-related-entity-component.d.ts.map +1 -0
- package/dist/Angular/user-view-grid-related-entity-component.js +85 -0
- package/dist/Angular/user-view-grid-related-entity-component.js.map +1 -0
- package/dist/Config/config.d.ts +79 -0
- package/dist/Config/config.d.ts.map +1 -1
- package/dist/Config/config.js +174 -172
- package/dist/Config/config.js.map +1 -1
- package/dist/Config/db-connection.d.ts +1 -1
- package/dist/Config/db-connection.d.ts.map +1 -1
- package/dist/Config/db-connection.js +6 -33
- package/dist/Config/db-connection.js.map +1 -1
- package/dist/Database/dbSchema.js +28 -35
- package/dist/Database/dbSchema.js.map +1 -1
- package/dist/Database/manage-metadata.d.ts +25 -9
- package/dist/Database/manage-metadata.d.ts.map +1 -1
- package/dist/Database/manage-metadata.js +438 -316
- package/dist/Database/manage-metadata.js.map +1 -1
- package/dist/Database/reorder-columns.d.ts +1 -1
- package/dist/Database/reorder-columns.d.ts.map +1 -1
- package/dist/Database/reorder-columns.js +1 -5
- package/dist/Database/reorder-columns.js.map +1 -1
- package/dist/Database/sql.d.ts +1 -1
- package/dist/Database/sql.d.ts.map +1 -1
- package/dist/Database/sql.js +67 -98
- package/dist/Database/sql.js.map +1 -1
- package/dist/Database/sql_codegen.d.ts +5 -2
- package/dist/Database/sql_codegen.d.ts.map +1 -1
- package/dist/Database/sql_codegen.js +344 -268
- package/dist/Database/sql_codegen.js.map +1 -1
- package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts +110 -0
- package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts.map +1 -0
- package/dist/Manifest/GenerateClassRegistrationsManifest.js +632 -0
- package/dist/Manifest/GenerateClassRegistrationsManifest.js.map +1 -0
- package/dist/Misc/action_subclasses_codegen.d.ts.map +1 -1
- package/dist/Misc/action_subclasses_codegen.js +15 -26
- package/dist/Misc/action_subclasses_codegen.js.map +1 -1
- package/dist/Misc/advanced_generation.d.ts +1 -1
- package/dist/Misc/advanced_generation.js +34 -40
- package/dist/Misc/advanced_generation.js.map +1 -1
- package/dist/Misc/createNewUser.d.ts +1 -1
- package/dist/Misc/createNewUser.js +22 -26
- package/dist/Misc/createNewUser.js.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.d.ts +2 -2
- package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.js +33 -40
- package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
- package/dist/Misc/graphql_server_codegen.js +36 -41
- package/dist/Misc/graphql_server_codegen.js.map +1 -1
- package/dist/Misc/runCommand.d.ts +1 -1
- package/dist/Misc/runCommand.js +13 -20
- package/dist/Misc/runCommand.js.map +1 -1
- package/dist/Misc/sql_logging.d.ts +7 -1
- package/dist/Misc/sql_logging.d.ts.map +1 -1
- package/dist/Misc/sql_logging.js +40 -53
- package/dist/Misc/sql_logging.js.map +1 -1
- package/dist/Misc/status_logging.js +45 -60
- package/dist/Misc/status_logging.js.map +1 -1
- package/dist/Misc/system_integrity.d.ts +1 -1
- package/dist/Misc/system_integrity.d.ts.map +1 -1
- package/dist/Misc/system_integrity.js +12 -16
- package/dist/Misc/system_integrity.js.map +1 -1
- package/dist/Misc/temp_batch_file.js +15 -22
- package/dist/Misc/temp_batch_file.js.map +1 -1
- package/dist/Misc/util.d.ts.map +1 -1
- package/dist/Misc/util.js +17 -28
- package/dist/Misc/util.js.map +1 -1
- package/dist/index.d.ts +21 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -40
- package/dist/index.js.map +1 -1
- package/dist/runCodeGen.d.ts +1 -0
- package/dist/runCodeGen.d.ts.map +1 -1
- package/dist/runCodeGen.js +151 -178
- package/dist/runCodeGen.js.map +1 -1
- package/package.json +24 -21
package/README.md
CHANGED
|
@@ -542,6 +542,61 @@ type Customer {
|
|
|
542
542
|
|
|
543
543
|
**Total: 500+ lines of production code from 6 lines of SQL.**
|
|
544
544
|
|
|
545
|
+
### Class Registrations Manifest Generation
|
|
546
|
+
|
|
547
|
+
Generates an import manifest that prevents tree-shaking of `@RegisterClass` decorated classes. The tool walks an app's transitive dependency tree and produces a tailored manifest for each application.
|
|
548
|
+
|
|
549
|
+
**Why this matters:** Decorators like `@RegisterClass` only execute if their file is imported. Bundlers can tree-shake entire files that appear unused, silently breaking MemberJunction's class factory system. The manifest ensures all registered classes are imported.
|
|
550
|
+
|
|
551
|
+
**How it works:**
|
|
552
|
+
1. Reads the app's `package.json` and walks the full transitive dependency tree
|
|
553
|
+
2. Scans each dependency's `src/**/*.ts` for `@RegisterClass` decorators using the TypeScript Compiler API
|
|
554
|
+
3. Generates a manifest file with `import` statements for every package that contains registered classes
|
|
555
|
+
|
|
556
|
+
**Per-app results (verified):**
|
|
557
|
+
|
|
558
|
+
| App | Deps Walked | Packages with @RegisterClass | Total Classes |
|
|
559
|
+
|-----|-------------|------------------------------|---------------|
|
|
560
|
+
| MJAPI | 985 | 54 | 715 |
|
|
561
|
+
| MJExplorer | 1179 | 17 | 721 |
|
|
562
|
+
|
|
563
|
+
**Programmatic usage:**
|
|
564
|
+
```typescript
|
|
565
|
+
import { generateClassRegistrationsManifest } from '@memberjunction/codegen-lib';
|
|
566
|
+
|
|
567
|
+
const result = await generateClassRegistrationsManifest({
|
|
568
|
+
outputPath: './src/generated/class-registrations-manifest.ts',
|
|
569
|
+
appDir: './packages/MJAPI', // defaults to process.cwd()
|
|
570
|
+
filterBaseClasses: ['BaseEngine'], // optional filter
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
if (result.success) {
|
|
574
|
+
console.log(`${result.packages.length} packages, ${result.classes.length} classes`);
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
**CLI usage (via MJCLI):**
|
|
579
|
+
```bash
|
|
580
|
+
mj codegen manifest --output ./src/generated/class-registrations-manifest.ts
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
See the [MJCLI README](../MJCLI/README.md) for full CLI documentation.
|
|
584
|
+
|
|
585
|
+
**Pre-built manifests for npm distribution:**
|
|
586
|
+
|
|
587
|
+
When MJ packages are installed via npm, consumers only receive `dist/` (no `src/`), so the manifest generator can't scan them. To solve this, MJ ships **pre-built manifests** inside the bootstrap packages:
|
|
588
|
+
|
|
589
|
+
- `@memberjunction/server-bootstrap` — 623 server-side classes from 54 packages
|
|
590
|
+
- `@memberjunction/ng-bootstrap` — 383 Angular classes from 14 packages
|
|
591
|
+
|
|
592
|
+
External consumers import the pre-built manifest for MJ classes, then generate a supplemental manifest for their own classes using `--exclude-packages @memberjunction`.
|
|
593
|
+
|
|
594
|
+
For complete setup instructions, CLI reference, and troubleshooting, see the **[Class Manifest Guide](CLASS_MANIFEST_GUIDE.md)**.
|
|
595
|
+
|
|
596
|
+
**Example output:**
|
|
597
|
+
- [MJAPI manifest (server-side)](EXAMPLE_MANIFEST_MJAPI.md) — 54 packages, 715 classes (AI providers, actions, encryption, scheduling, storage, etc.)
|
|
598
|
+
- [MJExplorer manifest (client-side)](EXAMPLE_MANIFEST_MJEXPLORER.md) — 17 packages, 721 classes (Angular components, dashboards, forms, etc.)
|
|
599
|
+
|
|
545
600
|
## API Reference
|
|
546
601
|
|
|
547
602
|
### Core Functions
|
|
@@ -552,7 +607,7 @@ await runCodeGen();
|
|
|
552
607
|
|
|
553
608
|
// Generate specific components
|
|
554
609
|
await generateEntitySubClasses(options);
|
|
555
|
-
await generateAngularEntityCode(options);
|
|
610
|
+
await generateAngularEntityCode(options);
|
|
556
611
|
await generateSQLScripts(options);
|
|
557
612
|
await generateGraphQLServerCode(options);
|
|
558
613
|
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EntityInfo, EntityFieldInfo, GeneratedFormSectionType, UserInfo, EntityRelationshipInfo } from '@memberjunction/core';
|
|
2
|
-
import { GenerationResult } from './related-entity-components';
|
|
2
|
+
import { GenerationResult } from './related-entity-components.js';
|
|
3
3
|
/**
|
|
4
4
|
* Represents metadata about an Angular form section that is generated for an entity
|
|
5
5
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"angular-codegen.d.ts","sourceRoot":"","sources":["../../src/Angular/angular-codegen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,wBAAwB,EAAyD,QAAQ,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAKtL,OAAO,EAAE,gBAAgB,EAA8C,MAAM,6BAA6B,CAAC;AAG3G;;GAEG;AACH,qBAAa,sBAAsB;IAC/B;;OAEG;IACH,IAAI,EAAG,wBAAwB,CAAC;IAEhC;;OAEG;IACH,IAAI,EAAG,MAAM,CAAC;IAEd;;OAEG;IACH,OAAO,EAAG,MAAM,CAAC;IAEjB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAE3B;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAElC;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAS;IAElC;;OAEG;IACH,4BAA4B,CAAC,EAAE,mBAAmB,GAAG,kBAAkB,CAAqB;IAE5F;;OAEG;IACH,eAAe,CAAC,EAAE,gBAAgB,CAAC;IAEnC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,qBAAa,0BAA0B;IACnC;;;;;;;OAOG;IACU,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAoFxI;;;;;;;;;OASG;IACH,SAAS,CAAC,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,EAAE,EAC1B,cAAc,EAAE;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,0BAA0B,EAAE;YAAC,aAAa,EAAE,MAAM,CAAC;YAAC,eAAe,EAAE,MAAM,CAAA;SAAC,EAAE,CAAA;KAAC,EAAE,EACzH,0BAA0B,EAAE;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAC,EAAE,EAClE,QAAQ,EAAE,sBAAsB,EAAE,EAClC,YAAY,EAAE,MAAM,EACpB,sBAAsB,GAAE,MAAW,GAAG,MAAM;
|
|
1
|
+
{"version":3,"file":"angular-codegen.d.ts","sourceRoot":"","sources":["../../src/Angular/angular-codegen.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,84 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const status_logging_1 = require("../Misc/status_logging");
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const config_1 = require("../Config/config");
|
|
12
|
-
const related_entity_components_1 = require("./related-entity-components");
|
|
13
|
-
const util_1 = require("../Misc/util");
|
|
1
|
+
import { GeneratedFormSectionType, EntityFieldTSType, EntityFieldValueListType, Metadata } from '@memberjunction/core';
|
|
2
|
+
import { logError, logStatus } from '../Misc/status_logging.js';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { mjCoreSchema, outputOptionValue, configInfo } from '../Config/config.js';
|
|
6
|
+
import { RelatedEntityDisplayComponentGeneratorBase } from './related-entity-components.js';
|
|
7
|
+
import { sortBySequenceAndCreatedAt } from '../Misc/util.js';
|
|
14
8
|
/**
|
|
15
9
|
* Represents metadata about an Angular form section that is generated for an entity
|
|
16
10
|
*/
|
|
17
|
-
class AngularFormSectionInfo {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*/
|
|
29
|
-
TabCode;
|
|
30
|
-
/**
|
|
31
|
-
* The TypeScript class name for the section component
|
|
32
|
-
*/
|
|
33
|
-
ClassName;
|
|
34
|
-
/**
|
|
35
|
-
* The filename where the section component will be saved
|
|
36
|
-
*/
|
|
37
|
-
FileName;
|
|
38
|
-
/**
|
|
39
|
-
* The complete TypeScript component code for the section
|
|
40
|
-
*/
|
|
41
|
-
ComponentCode;
|
|
42
|
-
/**
|
|
43
|
-
* Array of entity fields that belong to this section
|
|
44
|
-
*/
|
|
45
|
-
Fields;
|
|
46
|
-
/**
|
|
47
|
-
* The filename without the .ts extension
|
|
48
|
-
*/
|
|
49
|
-
FileNameWithoutExtension;
|
|
50
|
-
/**
|
|
51
|
-
* The class name of the entity this section belongs to
|
|
52
|
-
*/
|
|
53
|
-
EntityClassName;
|
|
54
|
-
/**
|
|
55
|
-
* Indicates if this section represents a related entity tab
|
|
56
|
-
*/
|
|
57
|
-
IsRelatedEntity = false;
|
|
58
|
-
/**
|
|
59
|
-
* Specifies where related entity tabs should be displayed relative to field tabs
|
|
60
|
-
*/
|
|
61
|
-
RelatedEntityDisplayLocation = 'After Field Tabs';
|
|
62
|
-
/**
|
|
63
|
-
* The generation result for related entity components
|
|
64
|
-
*/
|
|
65
|
-
GeneratedOutput;
|
|
66
|
-
/**
|
|
67
|
-
* The minimum sequence number from fields in this section (used for sorting)
|
|
68
|
-
*/
|
|
69
|
-
MinSequence;
|
|
70
|
-
/**
|
|
71
|
-
* The unique camelCase key used for this section in the sectionsExpanded object
|
|
72
|
-
*/
|
|
73
|
-
UniqueKey;
|
|
11
|
+
export class AngularFormSectionInfo {
|
|
12
|
+
constructor() {
|
|
13
|
+
/**
|
|
14
|
+
* Indicates if this section represents a related entity tab
|
|
15
|
+
*/
|
|
16
|
+
this.IsRelatedEntity = false;
|
|
17
|
+
/**
|
|
18
|
+
* Specifies where related entity tabs should be displayed relative to field tabs
|
|
19
|
+
*/
|
|
20
|
+
this.RelatedEntityDisplayLocation = 'After Field Tabs';
|
|
21
|
+
}
|
|
74
22
|
}
|
|
75
|
-
exports.AngularFormSectionInfo = AngularFormSectionInfo;
|
|
76
23
|
/**
|
|
77
24
|
* Base class for generating Angular client code for MemberJunction entities.
|
|
78
25
|
* This class handles the generation of Angular components, forms, and modules based on entity metadata.
|
|
79
26
|
* You can sub-class this class to create your own Angular client code generator logic.
|
|
80
27
|
*/
|
|
81
|
-
class AngularClientGeneratorBase {
|
|
28
|
+
export class AngularClientGeneratorBase {
|
|
29
|
+
constructor() {
|
|
30
|
+
/**
|
|
31
|
+
* Base name used for generating sub-module names
|
|
32
|
+
* @protected
|
|
33
|
+
*/
|
|
34
|
+
this.subModule_BaseName = 'GeneratedForms_SubModule_';
|
|
35
|
+
}
|
|
82
36
|
/**
|
|
83
37
|
* Main entry point for generating Angular code for a collection of entities
|
|
84
38
|
* @param entities Array of EntityInfo objects to generate Angular code for
|
|
@@ -89,24 +43,24 @@ class AngularClientGeneratorBase {
|
|
|
89
43
|
*/
|
|
90
44
|
async generateAngularCode(entities, directory, modulePrefix, contextUser) {
|
|
91
45
|
try {
|
|
92
|
-
const entityPath =
|
|
46
|
+
const entityPath = path.join(directory, 'Entities');
|
|
93
47
|
//const classMapEntries: string[] = [];
|
|
94
48
|
const componentImports = [];
|
|
95
49
|
const relatedEntityModuleImports = [];
|
|
96
50
|
const componentNames = [];
|
|
97
51
|
const sections = [];
|
|
98
|
-
if (!
|
|
99
|
-
|
|
52
|
+
if (!fs.existsSync(entityPath))
|
|
53
|
+
fs.mkdirSync(entityPath, { recursive: true }); // create the directory if it doesn't exist
|
|
100
54
|
for (let i = 0; i < entities.length; ++i) {
|
|
101
55
|
const entity = entities[i];
|
|
102
56
|
if (entity.PrimaryKeys && entity.PrimaryKeys.length > 0 && entity.IncludeInAPI) {
|
|
103
|
-
const thisEntityPath =
|
|
104
|
-
if (!
|
|
105
|
-
|
|
57
|
+
const thisEntityPath = path.join(entityPath, entity.ClassName);
|
|
58
|
+
if (!fs.existsSync(thisEntityPath))
|
|
59
|
+
fs.mkdirSync(thisEntityPath, { recursive: true }); // create the directory if it doesn't exist
|
|
106
60
|
const { htmlCode, additionalSections, relatedEntitySections } = await this.generateSingleEntityHTMLForAngular(entity, contextUser);
|
|
107
61
|
const tsCode = this.generateSingleEntityTypeScriptForAngular(entity, additionalSections, relatedEntitySections);
|
|
108
|
-
|
|
109
|
-
|
|
62
|
+
fs.writeFileSync(path.join(thisEntityPath, `${entity.ClassName.toLowerCase()}.form.component.ts`), tsCode);
|
|
63
|
+
fs.writeFileSync(path.join(thisEntityPath, `${entity.ClassName.toLowerCase()}.form.component.html`), htmlCode);
|
|
110
64
|
// Sections are now inline in HTML templates, no separate files needed
|
|
111
65
|
// Just track them for module generation purposes
|
|
112
66
|
if (additionalSections.length > 0) {
|
|
@@ -115,7 +69,7 @@ class AngularClientGeneratorBase {
|
|
|
115
69
|
});
|
|
116
70
|
}
|
|
117
71
|
const componentName = `${entity.ClassName}FormComponent`;
|
|
118
|
-
componentImports.push(`import { ${componentName}
|
|
72
|
+
componentImports.push(`import { ${componentName} } from "./Entities/${entity.ClassName}/${entity.ClassName.toLowerCase()}.form.component";`);
|
|
119
73
|
const currentComponentDistinctRelatedEntityClassNames = [];
|
|
120
74
|
relatedEntitySections.forEach(s => s.GeneratedOutput.Component.ImportItems.forEach(i => {
|
|
121
75
|
if (!currentComponentDistinctRelatedEntityClassNames.find(ii => ii.itemClassName === i.ClassName))
|
|
@@ -140,16 +94,16 @@ class AngularClientGeneratorBase {
|
|
|
140
94
|
// now the imports are good
|
|
141
95
|
}
|
|
142
96
|
else {
|
|
143
|
-
|
|
97
|
+
logStatus(` Entity ${entity.Name} does not have a primary key or is not included in the API, skipping code generation for this entity`);
|
|
144
98
|
}
|
|
145
99
|
}
|
|
146
|
-
const maxComponentsPerModule =
|
|
100
|
+
const maxComponentsPerModule = outputOptionValue('Angular', 'maxComponentsPerModule', 25);
|
|
147
101
|
const moduleCode = this.generateAngularModule(componentImports, componentNames, relatedEntityModuleImports, sections, modulePrefix, maxComponentsPerModule);
|
|
148
|
-
|
|
102
|
+
fs.writeFileSync(path.join(directory, 'generated-forms.module.ts'), moduleCode);
|
|
149
103
|
return true;
|
|
150
104
|
}
|
|
151
105
|
catch (err) {
|
|
152
|
-
|
|
106
|
+
logError(err);
|
|
153
107
|
return false;
|
|
154
108
|
}
|
|
155
109
|
}
|
|
@@ -205,14 +159,9 @@ ${relatedEntityModuleImports.filter(remi => remi.library.trim().toLowerCase() !=
|
|
|
205
159
|
.join('\n')}
|
|
206
160
|
${moduleCode}
|
|
207
161
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// code do NOTHING - the point is to prevent the code from being eliminated during tree shaking
|
|
212
|
-
// since it is dynamically instantiated on demand, and the Angular compiler has no way to know that,
|
|
213
|
-
// in production builds tree shaking will eliminate the code unless we do this
|
|
214
|
-
${componentNames.map(c => `Load${c.componentName}();`).join('\n ')}
|
|
215
|
-
}
|
|
162
|
+
// Note: LoadXXXGeneratedForms() functions have been removed. Tree-shaking prevention
|
|
163
|
+
// is now handled by the pre-built class registration manifest system.
|
|
164
|
+
// See packages/CodeGenLib/CLASS_MANIFEST_GUIDE.md for details.
|
|
216
165
|
`;
|
|
217
166
|
}
|
|
218
167
|
/**
|
|
@@ -274,11 +223,6 @@ export class ${modulePrefix}GeneratedFormsModule { }`;
|
|
|
274
223
|
// so we need to combine the two into a single return value and send back to the caller
|
|
275
224
|
return subModules.join('\n\n') + '\n\n' + masterModuleCode;
|
|
276
225
|
}
|
|
277
|
-
/**
|
|
278
|
-
* Base name used for generating sub-module names
|
|
279
|
-
* @protected
|
|
280
|
-
*/
|
|
281
|
-
subModule_BaseName = 'GeneratedForms_SubModule_';
|
|
282
226
|
/**
|
|
283
227
|
* Get the base name for the sub-modules. Override this method to change the base name.
|
|
284
228
|
* @returns The base name for sub-modules (default: 'GeneratedForms_SubModule_')
|
|
@@ -355,7 +299,7 @@ export class ${this.SubModuleBaseName}${moduleNumber} { }
|
|
|
355
299
|
relatedEntitySections.filter(s => s.GeneratedOutput && s.GeneratedOutput.CodeOutput.length > 0)
|
|
356
300
|
.map(s => s.GeneratedOutput.CodeOutput.split("\n").map(l => ` ${l}`).join("\n")).join('\n') : '';
|
|
357
301
|
// Generate unique keys for all sections FIRST, then use them everywhere
|
|
358
|
-
const sectionsWithoutTop = additionalSections.filter(s => s.Type !==
|
|
302
|
+
const sectionsWithoutTop = additionalSections.filter(s => s.Type !== GeneratedFormSectionType.Top && s.Name);
|
|
359
303
|
const allSections = [...sectionsWithoutTop, ...relatedEntitySections];
|
|
360
304
|
// Assign unique keys to each section
|
|
361
305
|
const usedKeys = new Set();
|
|
@@ -390,13 +334,15 @@ export class ${this.SubModuleBaseName}${moduleNumber} { }
|
|
|
390
334
|
const sectionInitCode = sectionInitEntries.length > 0
|
|
391
335
|
? `\n\n override async ngOnInit() {\n await super.ngOnInit();\n this.initSections([\n${sectionInitEntries.join(',\n')}\n ]);\n }`
|
|
392
336
|
: '';
|
|
337
|
+
const entityPackageName = configInfo.entityPackageName || 'mj_generatedentities';
|
|
393
338
|
return `import { Component } from '@angular/core';
|
|
394
|
-
import { ${entityObjectClass}Entity } from '${entity.SchemaName ===
|
|
339
|
+
import { ${entityObjectClass}Entity } from '${entity.SchemaName === mjCoreSchema ? '@memberjunction/core-entities' : entityPackageName}';
|
|
395
340
|
import { RegisterClass } from '@memberjunction/global';
|
|
396
341
|
import { BaseFormComponent } from '@memberjunction/ng-base-forms';
|
|
397
342
|
${generationImports.length > 0 ? generationImports + '\n' : ''}
|
|
398
343
|
@RegisterClass(BaseFormComponent, '${entity.Name}') // Tell MemberJunction about this class
|
|
399
344
|
@Component({
|
|
345
|
+
standalone: false,
|
|
400
346
|
selector: 'gen-${entity.ClassName.toLowerCase()}-form',
|
|
401
347
|
templateUrl: './${entity.ClassName.toLowerCase()}.form.component.html'
|
|
402
348
|
})
|
|
@@ -404,9 +350,6 @@ export class ${entity.ClassName}FormComponent extends BaseFormComponent {
|
|
|
404
350
|
public record!: ${entityObjectClass}Entity;${generationInjectedCode.length > 0 ? '\n' + generationInjectedCode : ''}${sectionInitCode}
|
|
405
351
|
}
|
|
406
352
|
|
|
407
|
-
export function Load${entity.ClassName}FormComponent() {
|
|
408
|
-
// does nothing, but called to prevent tree-shaking from eliminating this component from the build
|
|
409
|
-
}
|
|
410
353
|
`;
|
|
411
354
|
}
|
|
412
355
|
/**
|
|
@@ -415,7 +358,7 @@ export function Load${entity.ClassName}FormComponent() {
|
|
|
415
358
|
* @returns True if the entity has top area fields, false otherwise
|
|
416
359
|
*/
|
|
417
360
|
entityHasTopArea(entity) {
|
|
418
|
-
return entity.Fields.some(f => f.GeneratedFormSectionType ===
|
|
361
|
+
return entity.Fields.some(f => f.GeneratedFormSectionType === GeneratedFormSectionType.Top);
|
|
419
362
|
}
|
|
420
363
|
/**
|
|
421
364
|
* Generates HTML for the top area section of an entity form
|
|
@@ -468,23 +411,23 @@ export function Load${entity.ClassName}FormComponent() {
|
|
|
468
411
|
generateAngularAdditionalSections(entity, startIndex, categoryIcons) {
|
|
469
412
|
const sections = [];
|
|
470
413
|
let index = startIndex;
|
|
471
|
-
const sortedFields =
|
|
414
|
+
const sortedFields = sortBySequenceAndCreatedAt(entity.Fields);
|
|
472
415
|
for (const field of sortedFields) {
|
|
473
416
|
if (field.IncludeInGeneratedForm) {
|
|
474
|
-
if (field.GeneratedFormSectionType ===
|
|
475
|
-
this.AddSectionIfNeeded(entity, sections,
|
|
476
|
-
else if (field.GeneratedFormSectionType ===
|
|
477
|
-
this.AddSectionIfNeeded(entity, sections,
|
|
478
|
-
else if (field.GeneratedFormSectionType ===
|
|
479
|
-
this.AddSectionIfNeeded(entity, sections,
|
|
417
|
+
if (field.GeneratedFormSectionType === GeneratedFormSectionType.Category && field.Category && field.Category !== '' && field.IncludeInGeneratedForm)
|
|
418
|
+
this.AddSectionIfNeeded(entity, sections, GeneratedFormSectionType.Category, field.Category, field.Sequence);
|
|
419
|
+
else if (field.GeneratedFormSectionType === GeneratedFormSectionType.Details)
|
|
420
|
+
this.AddSectionIfNeeded(entity, sections, GeneratedFormSectionType.Details, "Details", field.Sequence);
|
|
421
|
+
else if (field.GeneratedFormSectionType === GeneratedFormSectionType.Top)
|
|
422
|
+
this.AddSectionIfNeeded(entity, sections, GeneratedFormSectionType.Top, "Top", field.Sequence);
|
|
480
423
|
}
|
|
481
424
|
}
|
|
482
425
|
// Sort sections by minimum sequence (Top sections first, System sections last, then by MinSequence)
|
|
483
426
|
sections.sort((a, b) => {
|
|
484
427
|
// Top sections always first
|
|
485
|
-
if (a.Type ===
|
|
428
|
+
if (a.Type === GeneratedFormSectionType.Top)
|
|
486
429
|
return -1;
|
|
487
|
-
if (b.Type ===
|
|
430
|
+
if (b.Type === GeneratedFormSectionType.Top)
|
|
488
431
|
return 1;
|
|
489
432
|
// System sections always last (after related entities)
|
|
490
433
|
const aIsSystem = a.Name.toLowerCase() === 'system' || a.Name.toLowerCase() === 'system metadata';
|
|
@@ -502,14 +445,14 @@ export function Load${entity.ClassName}FormComponent() {
|
|
|
502
445
|
let sectionIndex = 0;
|
|
503
446
|
for (const section of sections) {
|
|
504
447
|
let sectionName = '';
|
|
505
|
-
if (section.Type ===
|
|
448
|
+
if (section.Type === GeneratedFormSectionType.Top) {
|
|
506
449
|
section.TabCode = this.generateTopAreaHTMLForAngular(entity);
|
|
507
450
|
sectionName = 'top-area';
|
|
508
451
|
}
|
|
509
452
|
else {
|
|
510
|
-
if (section.Type ===
|
|
453
|
+
if (section.Type === GeneratedFormSectionType.Category)
|
|
511
454
|
sectionName = this.stripWhiteSpace(section.Name.toLowerCase());
|
|
512
|
-
else if (section.Type ===
|
|
455
|
+
else if (section.Type === GeneratedFormSectionType.Details)
|
|
513
456
|
sectionName = 'details';
|
|
514
457
|
// Generate collapsible panel HTML inline instead of using separate components
|
|
515
458
|
const formHTML = this.generateSectionHTMLForAngular(entity, section);
|
|
@@ -539,7 +482,7 @@ ${indentedFormHTML}
|
|
|
539
482
|
</mj-collapsible-panel>`;
|
|
540
483
|
sectionIndex++;
|
|
541
484
|
}
|
|
542
|
-
if (section.Type !==
|
|
485
|
+
if (section.Type !== GeneratedFormSectionType.Top)
|
|
543
486
|
index++; // don't increment the tab index for TOP AREA, becuse it won't be rendered as a tab
|
|
544
487
|
}
|
|
545
488
|
return sections;
|
|
@@ -554,19 +497,19 @@ ${indentedFormHTML}
|
|
|
554
497
|
let html = '';
|
|
555
498
|
// figure out which fields will be in this section first
|
|
556
499
|
section.Fields = [];
|
|
557
|
-
const sortedFields =
|
|
500
|
+
const sortedFields = sortBySequenceAndCreatedAt(entity.Fields);
|
|
558
501
|
for (const field of sortedFields) {
|
|
559
502
|
if (field.IncludeInGeneratedForm) {
|
|
560
503
|
let bMatch = false;
|
|
561
|
-
if (field.GeneratedFormSectionType ===
|
|
504
|
+
if (field.GeneratedFormSectionType === GeneratedFormSectionType.Top && section.Type === GeneratedFormSectionType.Top) {
|
|
562
505
|
// match, include the field in the output
|
|
563
506
|
bMatch = true;
|
|
564
507
|
}
|
|
565
|
-
else if (field.GeneratedFormSectionType ===
|
|
508
|
+
else if (field.GeneratedFormSectionType === GeneratedFormSectionType.Category && field.Category && section.Name && field.Category.trim().toLowerCase() === section.Name.trim().toLowerCase()) {
|
|
566
509
|
// match, include the field in the output
|
|
567
510
|
bMatch = true;
|
|
568
511
|
}
|
|
569
|
-
else if (field.GeneratedFormSectionType ===
|
|
512
|
+
else if (field.GeneratedFormSectionType === GeneratedFormSectionType.Details && section.Type === GeneratedFormSectionType.Details) {
|
|
570
513
|
// match, include the field in the output
|
|
571
514
|
bMatch = true;
|
|
572
515
|
}
|
|
@@ -581,26 +524,26 @@ ${indentedFormHTML}
|
|
|
581
524
|
if (!field.ReadOnly) {
|
|
582
525
|
// first, check to see if we have a ValueListType != None, if so, generate a dropdown.
|
|
583
526
|
// If value list type is ListOrUserEntry, then generate a combobox, if ValueListType = List, then generate a dropdown
|
|
584
|
-
if (field.ValueListTypeEnum !==
|
|
527
|
+
if (field.ValueListTypeEnum !== EntityFieldValueListType.None) {
|
|
585
528
|
// build the possible values list
|
|
586
|
-
if (field.ValueListTypeEnum ===
|
|
529
|
+
if (field.ValueListTypeEnum === EntityFieldValueListType.ListOrUserEntry) {
|
|
587
530
|
// combo box
|
|
588
531
|
editControl = `combobox`;
|
|
589
532
|
}
|
|
590
|
-
else if (field.ValueListTypeEnum ===
|
|
533
|
+
else if (field.ValueListTypeEnum === EntityFieldValueListType.List) {
|
|
591
534
|
// dropdown
|
|
592
535
|
editControl = `dropdownlist`;
|
|
593
536
|
}
|
|
594
537
|
}
|
|
595
538
|
else {
|
|
596
539
|
// no value list, generate a text box, checkbox, or date picker
|
|
597
|
-
if (field.TSType ===
|
|
540
|
+
if (field.TSType === EntityFieldTSType.Boolean)
|
|
598
541
|
editControl = `checkbox`;
|
|
599
|
-
else if (field.TSType ===
|
|
542
|
+
else if (field.TSType === EntityFieldTSType.Date)
|
|
600
543
|
editControl = `datepicker`;
|
|
601
|
-
else if (field.TSType ===
|
|
544
|
+
else if (field.TSType === EntityFieldTSType.Number)
|
|
602
545
|
editControl = `numerictextbox`;
|
|
603
|
-
else if (field.TSType ===
|
|
546
|
+
else if (field.TSType === EntityFieldTSType.String) {
|
|
604
547
|
if (field.Length < 0 || field.MaxLength > 100) // length < 0 means nvarchar(max) or similar, so use textarea
|
|
605
548
|
editControl = `textarea`;
|
|
606
549
|
else
|
|
@@ -663,7 +606,7 @@ ${indentedFormHTML}
|
|
|
663
606
|
// if the fkeyField has wrapping [] then remove them
|
|
664
607
|
fkeyField = fkeyField.trim().replace('[', '').replace(']', '');
|
|
665
608
|
// let's get the actual entityInfo for the related entity so we can get the field and see if it has a display name
|
|
666
|
-
const md = new
|
|
609
|
+
const md = new Metadata();
|
|
667
610
|
const re = md.EntityByID(relatedEntity.RelatedEntityID);
|
|
668
611
|
const f = re.Fields.find(f => f.Name.trim().toLowerCase() === fkeyField.trim().toLowerCase());
|
|
669
612
|
if (f)
|
|
@@ -681,7 +624,7 @@ ${indentedFormHTML}
|
|
|
681
624
|
* @returns Promise resolving to array of related entity tab sections
|
|
682
625
|
*/
|
|
683
626
|
async generateRelatedEntityTabs(entity, startIndex, contextUser) {
|
|
684
|
-
const md = new
|
|
627
|
+
const md = new Metadata();
|
|
685
628
|
const tabs = [];
|
|
686
629
|
// Sort related entities by Sequence (user's explicit ordering), then by RelatedEntity name (stable tiebreaker)
|
|
687
630
|
const sortedRelatedEntities = entity.RelatedEntities
|
|
@@ -716,7 +659,7 @@ ${indentedFormHTML}
|
|
|
716
659
|
}
|
|
717
660
|
// Calculate section key before generation (may be replaced later if duplicate)
|
|
718
661
|
const sectionKey = this.camelCase(tabName);
|
|
719
|
-
const component = await
|
|
662
|
+
const component = await RelatedEntityDisplayComponentGeneratorBase.GetComponent(relatedEntity, contextUser);
|
|
720
663
|
const generateResults = await component.Generate({
|
|
721
664
|
Entity: entity,
|
|
722
665
|
RelationshipInfo: relatedEntity,
|
|
@@ -739,12 +682,14 @@ ${indentedFormHTML}
|
|
|
739
682
|
[formContext]="formContext"
|
|
740
683
|
[badgeCount]="GetSectionRowCount('${sectionKey}')"
|
|
741
684
|
[defaultExpanded]="false">
|
|
742
|
-
|
|
685
|
+
@if (record.IsSaved) {
|
|
686
|
+
<div>
|
|
743
687
|
${componentCodeWithIndent}
|
|
744
688
|
</div>
|
|
689
|
+
}
|
|
745
690
|
</mj-collapsible-panel>`;
|
|
746
691
|
tabs.push({
|
|
747
|
-
Type:
|
|
692
|
+
Type: GeneratedFormSectionType.Category,
|
|
748
693
|
IsRelatedEntity: true,
|
|
749
694
|
RelatedEntityDisplayLocation: relatedEntity.DisplayLocation,
|
|
750
695
|
Name: tabName,
|
|
@@ -959,7 +904,7 @@ ${this.innerCollapsiblePanelsHTML(additionalSections, relatedEntitySections)}
|
|
|
959
904
|
*/
|
|
960
905
|
innerCollapsiblePanelsHTML(additionalSections, relatedEntitySections) {
|
|
961
906
|
// Filter out Top sections as they're handled separately
|
|
962
|
-
const sectionsToRender = additionalSections.filter(s => s.Type !==
|
|
907
|
+
const sectionsToRender = additionalSections.filter(s => s.Type !== GeneratedFormSectionType.Top);
|
|
963
908
|
// Order: before-panels, field-panels, after-panels
|
|
964
909
|
// The RecordFormContainer handles the related-entity-grid wrapper via named slots
|
|
965
910
|
const beforePanels = relatedEntitySections.filter(s => s.RelatedEntityDisplayLocation === 'Before Field Tabs');
|
|
@@ -1002,7 +947,7 @@ ${this.innerCollapsiblePanelsHTML(additionalSections, relatedEntitySections)}
|
|
|
1002
947
|
const relatedEntityAfterFieldTabs = relatedEntitySections.filter(s => s.RelatedEntityDisplayLocation === 'After Field Tabs');
|
|
1003
948
|
return ` <mj-tabstrip (TabSelected)="onTabSelect($event)" (ResizeContainer)="InvokeManualResize()">
|
|
1004
949
|
${relatedEntityBeforeFieldTabs ? relatedEntityBeforeFieldTabs.map(s => s.TabCode).join('\n') : ''}
|
|
1005
|
-
${additionalSections ? additionalSections.filter(s => s.Type !==
|
|
950
|
+
${additionalSections ? additionalSections.filter(s => s.Type !== GeneratedFormSectionType.Top).map(s => s.TabCode).join('\n ') : ''}
|
|
1006
951
|
${relatedEntityAfterFieldTabs ? relatedEntityAfterFieldTabs.map(s => s.TabCode).join('\n') : ''}
|
|
1007
952
|
</mj-tabstrip>`;
|
|
1008
953
|
}
|
|
@@ -1022,5 +967,4 @@ ${this.innerCollapsiblePanelsHTML(additionalSections, relatedEntitySections)}
|
|
|
1022
967
|
return htmlCode;
|
|
1023
968
|
}
|
|
1024
969
|
}
|
|
1025
|
-
exports.AngularClientGeneratorBase = AngularClientGeneratorBase;
|
|
1026
970
|
//# sourceMappingURL=angular-codegen.js.map
|