@emfts/codegen 0.0.1-next.8 → 0.0.2-next.2

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.
Files changed (152) hide show
  1. package/dist/cli/commands/generate.d.ts.map +1 -1
  2. package/dist/cli/commands/generate.js +12 -12
  3. package/dist/cli/commands/generate.js.map +1 -1
  4. package/dist/cli/commands/init.d.ts +6 -0
  5. package/dist/cli/commands/init.d.ts.map +1 -1
  6. package/dist/cli/commands/init.js +69 -16
  7. package/dist/cli/commands/init.js.map +1 -1
  8. package/dist/cli/import-mappings.d.ts +5 -0
  9. package/dist/cli/import-mappings.d.ts.map +1 -0
  10. package/dist/cli/import-mappings.js +16 -0
  11. package/dist/cli/import-mappings.js.map +1 -0
  12. package/dist/genconfig/GenConfig.d.ts +14 -0
  13. package/dist/genconfig/GenConfig.d.ts.map +1 -1
  14. package/dist/genconfig/GenConfig.js.map +1 -1
  15. package/dist/genconfig/GenConfigConverter.d.ts.map +1 -1
  16. package/dist/genconfig/GenConfigConverter.js +4 -1
  17. package/dist/genconfig/GenConfigConverter.js.map +1 -1
  18. package/dist/genconfig/GenConfigLoader.d.ts +4 -4
  19. package/dist/genconfig/GenConfigLoader.d.ts.map +1 -1
  20. package/dist/genconfig/GenConfigLoader.js +38 -27
  21. package/dist/genconfig/GenConfigLoader.js.map +1 -1
  22. package/dist/genconfig/model/ClassDefaults.d.ts +12 -0
  23. package/dist/genconfig/model/ClassDefaults.d.ts.map +1 -0
  24. package/dist/genconfig/model/ClassDefaults.js +8 -0
  25. package/dist/genconfig/model/ClassDefaults.js.map +1 -0
  26. package/dist/genconfig/model/ClassDefaultsImpl.d.ts +46 -0
  27. package/dist/genconfig/model/ClassDefaultsImpl.d.ts.map +1 -0
  28. package/dist/genconfig/model/ClassDefaultsImpl.js +205 -0
  29. package/dist/genconfig/model/ClassDefaultsImpl.js.map +1 -0
  30. package/dist/genconfig/model/ClassOverride.d.ts +15 -0
  31. package/dist/genconfig/model/ClassOverride.d.ts.map +1 -0
  32. package/dist/genconfig/model/ClassOverride.js +8 -0
  33. package/dist/genconfig/model/ClassOverride.js.map +1 -0
  34. package/dist/genconfig/model/ClassOverrideImpl.d.ts +53 -0
  35. package/dist/genconfig/model/ClassOverrideImpl.d.ts.map +1 -0
  36. package/dist/genconfig/model/ClassOverrideImpl.js +248 -0
  37. package/dist/genconfig/model/ClassOverrideImpl.js.map +1 -0
  38. package/dist/genconfig/model/FeatureDefaults.d.ts +11 -0
  39. package/dist/genconfig/model/FeatureDefaults.d.ts.map +1 -0
  40. package/dist/genconfig/model/FeatureDefaults.js +8 -0
  41. package/dist/genconfig/model/FeatureDefaults.js.map +1 -0
  42. package/dist/genconfig/model/FeatureDefaultsImpl.d.ts +39 -0
  43. package/dist/genconfig/model/FeatureDefaultsImpl.d.ts.map +1 -0
  44. package/dist/genconfig/model/FeatureDefaultsImpl.js +136 -0
  45. package/dist/genconfig/model/FeatureDefaultsImpl.js.map +1 -0
  46. package/dist/genconfig/model/FeatureOverride.d.ts +14 -0
  47. package/dist/genconfig/model/FeatureOverride.d.ts.map +1 -0
  48. package/dist/genconfig/model/FeatureOverride.js +8 -0
  49. package/dist/genconfig/model/FeatureOverride.js.map +1 -0
  50. package/dist/genconfig/model/FeatureOverrideImpl.d.ts +51 -0
  51. package/dist/genconfig/model/FeatureOverrideImpl.d.ts.map +1 -0
  52. package/dist/genconfig/model/FeatureOverrideImpl.js +241 -0
  53. package/dist/genconfig/model/FeatureOverrideImpl.js.map +1 -0
  54. package/dist/genconfig/model/GenConfig.d.ts +21 -0
  55. package/dist/genconfig/model/GenConfig.d.ts.map +1 -0
  56. package/dist/genconfig/model/GenConfig.js +8 -0
  57. package/dist/genconfig/model/GenConfig.js.map +1 -0
  58. package/dist/genconfig/model/GenConfigFactory.d.ts +56 -0
  59. package/dist/genconfig/model/GenConfigFactory.d.ts.map +1 -0
  60. package/dist/genconfig/model/GenConfigFactory.js +108 -0
  61. package/dist/genconfig/model/GenConfigFactory.js.map +1 -0
  62. package/dist/genconfig/model/GenConfigImpl.d.ts +62 -0
  63. package/dist/genconfig/model/GenConfigImpl.d.ts.map +1 -0
  64. package/dist/genconfig/model/GenConfigImpl.js +283 -0
  65. package/dist/genconfig/model/GenConfigImpl.js.map +1 -0
  66. package/dist/genconfig/model/GenConfigPackage.d.ts +68 -0
  67. package/dist/genconfig/model/GenConfigPackage.d.ts.map +1 -0
  68. package/dist/genconfig/model/GenConfigPackage.js +463 -0
  69. package/dist/genconfig/model/GenConfigPackage.js.map +1 -0
  70. package/dist/genconfig/model/GenerationMode.d.ts +19 -0
  71. package/dist/genconfig/model/GenerationMode.d.ts.map +1 -0
  72. package/dist/genconfig/model/GenerationMode.js +34 -0
  73. package/dist/genconfig/model/GenerationMode.js.map +1 -0
  74. package/dist/genconfig/model/GenerationSettings.d.ts +14 -0
  75. package/dist/genconfig/model/GenerationSettings.d.ts.map +1 -0
  76. package/dist/genconfig/model/GenerationSettings.js +8 -0
  77. package/dist/genconfig/model/GenerationSettings.js.map +1 -0
  78. package/dist/genconfig/model/GenerationSettingsImpl.d.ts +51 -0
  79. package/dist/genconfig/model/GenerationSettingsImpl.d.ts.map +1 -0
  80. package/dist/genconfig/model/GenerationSettingsImpl.js +241 -0
  81. package/dist/genconfig/model/GenerationSettingsImpl.js.map +1 -0
  82. package/dist/genconfig/model/PackageSettings.d.ts +13 -0
  83. package/dist/genconfig/model/PackageSettings.d.ts.map +1 -0
  84. package/dist/genconfig/model/PackageSettings.js +8 -0
  85. package/dist/genconfig/model/PackageSettings.js.map +1 -0
  86. package/dist/genconfig/model/PackageSettingsImpl.d.ts +50 -0
  87. package/dist/genconfig/model/PackageSettingsImpl.d.ts.map +1 -0
  88. package/dist/genconfig/model/PackageSettingsImpl.js +240 -0
  89. package/dist/genconfig/model/PackageSettingsImpl.js.map +1 -0
  90. package/dist/genconfig/model/PropertyMode.d.ts +19 -0
  91. package/dist/genconfig/model/PropertyMode.d.ts.map +1 -0
  92. package/dist/genconfig/model/PropertyMode.js +34 -0
  93. package/dist/genconfig/model/PropertyMode.js.map +1 -0
  94. package/dist/genconfig/model/ReferencedPackage.d.ts +10 -0
  95. package/dist/genconfig/model/ReferencedPackage.d.ts.map +1 -0
  96. package/dist/genconfig/model/ReferencedPackage.js +8 -0
  97. package/dist/genconfig/model/ReferencedPackage.js.map +1 -0
  98. package/dist/genconfig/model/ReferencedPackageImpl.d.ts +38 -0
  99. package/dist/genconfig/model/ReferencedPackageImpl.d.ts.map +1 -0
  100. package/dist/genconfig/model/ReferencedPackageImpl.js +135 -0
  101. package/dist/genconfig/model/ReferencedPackageImpl.js.map +1 -0
  102. package/dist/genconfig/model/index.d.ts +21 -0
  103. package/dist/genconfig/model/index.d.ts.map +1 -0
  104. package/dist/genconfig/model/index.js +22 -0
  105. package/dist/genconfig/model/index.js.map +1 -0
  106. package/dist/generator/CodeGenerator.d.ts.map +1 -1
  107. package/dist/generator/CodeGenerator.js +2 -1
  108. package/dist/generator/CodeGenerator.js.map +1 -1
  109. package/dist/generator/ImportResolver.d.ts.map +1 -1
  110. package/dist/generator/ImportResolver.js +28 -5
  111. package/dist/generator/ImportResolver.js.map +1 -1
  112. package/dist/generator/TypeMapper.d.ts +2 -0
  113. package/dist/generator/TypeMapper.d.ts.map +1 -1
  114. package/dist/generator/TypeMapper.js +48 -3
  115. package/dist/generator/TypeMapper.js.map +1 -1
  116. package/dist/generator/modes/BaseGenerator.d.ts +6 -0
  117. package/dist/generator/modes/BaseGenerator.d.ts.map +1 -1
  118. package/dist/generator/modes/BaseGenerator.js +21 -1
  119. package/dist/generator/modes/BaseGenerator.js.map +1 -1
  120. package/dist/generator/modes/DecoratorGenerator.d.ts +5 -0
  121. package/dist/generator/modes/DecoratorGenerator.d.ts.map +1 -1
  122. package/dist/generator/modes/DecoratorGenerator.js +11 -2
  123. package/dist/generator/modes/DecoratorGenerator.js.map +1 -1
  124. package/dist/generator/modes/EmfGenerator.d.ts +1 -0
  125. package/dist/generator/modes/EmfGenerator.d.ts.map +1 -1
  126. package/dist/generator/modes/EmfGenerator.js +12 -4
  127. package/dist/generator/modes/EmfGenerator.js.map +1 -1
  128. package/dist/genmodel/GenModel.d.ts +2 -0
  129. package/dist/genmodel/GenModel.d.ts.map +1 -1
  130. package/dist/genmodel/GenModel.js.map +1 -1
  131. package/dist/index.js +15 -2
  132. package/dist/index.js.map +1 -1
  133. package/dist/loader/EcoreLoader.d.ts +4 -0
  134. package/dist/loader/EcoreLoader.d.ts.map +1 -1
  135. package/dist/loader/EcoreLoader.js +20 -0
  136. package/dist/loader/EcoreLoader.js.map +1 -1
  137. package/dist/templates/partials/decorator/class-file.ejs +99 -27
  138. package/dist/templates/partials/decorator/enum-file.ejs +1 -1
  139. package/dist/templates/partials/decorator/interface-file.ejs +1 -7
  140. package/dist/templates/partials/decorator/model-annotations.ejs +7 -6
  141. package/dist/templates/partials/decorator/package-file.ejs +4 -2
  142. package/dist/templates/partials/emf/class-file.ejs +97 -17
  143. package/dist/templates/partials/emf/factory-file.ejs +3 -3
  144. package/dist/templates/partials/emf/index-file.ejs +12 -6
  145. package/dist/templates/partials/emf/interface-file.ejs +5 -0
  146. package/dist/templates/partials/emf/package-file.ejs +42 -12
  147. package/dist/templates/partials/plain/class-file.ejs +1 -7
  148. package/dist/templates/partials/plain/interface-file.ejs +1 -7
  149. package/dist/templates/partials/plain/package-file.ejs +1 -1
  150. package/model/genconfig.ecore +21 -0
  151. package/model/genconfig.genconfig.xmi +11 -0
  152. package/package.json +3 -2
@@ -2,17 +2,87 @@
2
2
 
3
3
  <%# Generate imports for referenced types -%>
4
4
  <%
5
+ // GenModel documentation of a model element (class, feature, ...)
6
+ function getDocumentation(element) {
7
+ const ann = element?.getEAnnotation?.('http://www.eclipse.org/emf/2002/GenModel');
8
+ const doc = ann?.getDetails?.()?.getByKey?.('documentation');
9
+ return typeof doc === 'string' && doc.length > 0 ? doc : null;
10
+ }
11
+ // Custom decorator annotations (#31): an eAnnotation source of the form
12
+ // '<importable-specifier>/<DecoratorName>' (not an EMF source URI) becomes an
13
+ // import plus a decorator application with the details as options object
14
+ function getCustomDecorators(element) {
15
+ const decorators = [];
16
+ for (const ann of element?.getEAnnotations?.() ?? []) {
17
+ const source = ann.getSource?.();
18
+ if (!source || source.startsWith('http://') || source.startsWith('https://')) continue;
19
+ const slashIdx = source.lastIndexOf('/');
20
+ if (slashIdx <= 0) continue;
21
+ const name = source.substring(slashIdx + 1);
22
+ if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) continue;
23
+ const options = [];
24
+ for (const entry of ann.getDetails?.() ?? []) {
25
+ const key = entry.eGet?.(entry.eClass().getEStructuralFeature('key'));
26
+ const value = entry.eGet?.(entry.eClass().getEStructuralFeature('value'));
27
+ if (key == null) continue;
28
+ const keyOut = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
29
+ options.push(`${keyOut}: ${JSON.stringify(value ?? '')}`);
30
+ }
31
+ decorators.push({ name, specifier: source.substring(0, slashIdx), options: `{${options.join(', ')}}` });
32
+ }
33
+ return decorators;
34
+ }
35
+ // Determine which of the annotation decorators the class actually uses (#32)
36
+ const classDoc = getDocumentation(eClass);
37
+ let usesDocumentation = !!classDoc;
38
+ let usesAttribute = false, usesReference = false, usesEnum = false;
39
+ for (const genFeature of features) {
40
+ const f = genFeature.ecoreFeature;
41
+ if (getDocumentation(f)) usesDocumentation = true;
42
+ if (f.getEType() && typeMapper.isEEnum(f.getEType())) usesEnum = true;
43
+ else if (typeMapper.isReference(f)) usesReference = true;
44
+ else usesAttribute = true;
45
+ }
46
+ for (const genOp of operations) {
47
+ if (getDocumentation(genOp.ecoreOperation)) usesDocumentation = true;
48
+ }
49
+ const annotationNames = [
50
+ ...(usesDocumentation ? ['Documentation'] : []),
51
+ ...(usesAttribute ? ['Attribute'] : []),
52
+ 'ModelClass',
53
+ ...(usesReference ? ['Reference'] : []),
54
+ ...(usesEnum ? ['Enum'] : [])
55
+ ];
56
+
57
+ // Merge all imports per module specifier: resolved type imports, the used
58
+ // annotation decorators, and custom decorator imports (#31, #32)
59
+ const mergedImports = new Map();
60
+ function addImport(specifier, name) {
61
+ if (!mergedImports.has(specifier)) mergedImports.set(specifier, new Set());
62
+ mergedImports.get(specifier).add(name);
63
+ }
5
64
  const imports = importResolver.resolveImportsForClass(eClass, genPackage, 'decorator');
6
65
  for (const imp of imports) {
7
66
  if (imp.typeName !== className && !imp.typeName.includes(',')) {
67
+ addImport(imp.importPath, imp.typeName);
68
+ }
69
+ }
70
+ for (const name of annotationNames) addImport(annotationsImport, name);
71
+ for (const genOp of operations) {
72
+ for (const dec of getCustomDecorators(genOp.ecoreOperation)) {
73
+ addImport(dec.specifier, dec.name);
74
+ }
75
+ }
76
+ for (const [specifier, names] of mergedImports) {
8
77
  -%>
9
- import { <%- imp.typeName %> } from '<%- imp.importPath %>';
78
+ import { <%- [...names].join(', ') %> } from '<%- specifier %>';
10
79
  <%
11
- }
12
80
  }
13
81
  -%>
14
- import { Documentation, Attribute, ModelClass, Reference, Enum } from './ModelAnnotations';
15
82
 
83
+ <% if (classDoc) { -%>
84
+ @Documentation(<%- JSON.stringify(classDoc) %>)
85
+ <% } -%>
16
86
  @ModelClass({ type: '<%- nsURI %>#//<%- className %>' })
17
87
  export <% if (isAbstract) { %>abstract <% } %>class <%- className %><%
18
88
  if (typeParameters && typeParameters.length > 0) {
@@ -25,8 +95,11 @@ if (typeParameters && typeParameters.length > 0) {
25
95
  if (superTypes && superTypes.length > 0) {
26
96
  const superNames = [];
27
97
  for (const st of superTypes) {
28
- if (st.getName) {
29
- superNames.push(st.getName());
98
+ // getName helper also resolves the name of an unresolved proxy from its
99
+ // URI fragment - a cross-package supertype must not lose its extends (#30)
100
+ const stName = getName(st);
101
+ if (stName) {
102
+ superNames.push(stName);
30
103
  }
31
104
  }
32
105
  if (superNames.length > 0) {
@@ -38,37 +111,31 @@ if (superTypes && superTypes.length > 0) {
38
111
  const feature = genFeature.ecoreFeature;
39
112
  const featureName = feature.getName();
40
113
  const featureType = typeMapper.mapFeatureBaseType(feature);
114
+ // Decorator argument is a runtime type NAME for metadata lookups -
115
+ // generic type arguments must not leak into it (#29). Derived from the
116
+ // mapped type, because an unresolved cross-package eType proxy has no getName
117
+ const decoratorTypeName = featureType.split('<')[0];
41
118
  const fullType = typeMapper.mapFeature(feature);
42
119
  const defaultValue = typeMapper.getDefaultValue(feature);
43
120
  const isMany = feature.isMany();
44
121
  const isRef = typeMapper.isReference(feature);
45
122
  const isEnum = feature.getEType() && typeMapper.isEEnum(feature.getEType());
46
123
 
47
- // Check for documentation annotation
48
- let doc = null;
49
- if (feature.getEAnnotations) {
50
- for (const ann of feature.getEAnnotations()) {
51
- const annSource = typeof ann.getSource === 'function' ? ann.getSource() : ann.source;
52
- if (annSource === 'http://www.eclipse.org/emf/2002/GenModel') {
53
- const details = typeof ann.getDetails === 'function' ? ann.getDetails() : ann.details;
54
- if (details && details.get) {
55
- doc = details.get('documentation');
56
- }
57
- }
58
- }
59
- }
124
+ // GenModel documentation as @Documentation decorator; JSON.stringify keeps
125
+ // multiline values and quotes a valid string literal
126
+ const doc = getDocumentation(feature);
60
127
  if (doc) {
61
128
  -%>
62
- @Documentation("<%- doc %>")
129
+ @Documentation(<%- JSON.stringify(doc) %>)
63
130
  <%
64
131
  }
65
132
  if (isEnum) {
66
133
  -%>
67
- @Enum('<%- featureType %>')
134
+ @Enum('<%- decoratorTypeName %>')
68
135
  <%
69
136
  } else if (isRef) {
70
137
  -%>
71
- @Reference('<%- featureType %>')
138
+ @Reference('<%- decoratorTypeName %>')
72
139
  <%
73
140
  } else {
74
141
  -%>
@@ -83,14 +150,19 @@ if (superTypes && superTypes.length > 0) {
83
150
  const op = genOp.ecoreOperation;
84
151
  const opName = op.getName();
85
152
  const returnType = op.getEType() ? typeMapper.mapClassifier(op.getEType()) : 'void';
86
- const params = [];
87
- for (const param of op.getEParameters()) {
88
- const paramName = param.getName();
89
- const paramType = typeMapper.mapClassifier(param.getEType());
90
- params.push(`${paramName}: ${paramType}`);
153
+ const opDoc = getDocumentation(op);
154
+ if (opDoc) {
155
+ -%>
156
+ @Documentation(<%- JSON.stringify(opDoc) %>)
157
+ <%
158
+ }
159
+ for (const dec of getCustomDecorators(op)) {
160
+ -%>
161
+ @<%- dec.name %>(<%- dec.options %>)
162
+ <%
91
163
  }
92
164
  -%>
93
- <%- opName %>(<%- params.join(', ') %>): <%- returnType %> {
165
+ <%- opName %>(<%- mapParams(op) %>): <%- returnType %> {
94
166
  throw new Error('<%- opName %> not implemented');
95
167
  }
96
168
 
@@ -28,7 +28,7 @@ export enum <%- enumName %> {
28
28
  const literal = literals[i];
29
29
  const literalName = getLiteralProp(literal, 'literal') || getLiteralProp(literal, 'name');
30
30
  const literalValue = getLiteralProp(literal, 'value');
31
- const value = useStringValues ? `'${literalName}'` : literalValue;
31
+ const value = useStringValues ? JSON.stringify(literalName) : literalValue;
32
32
  -%>
33
33
  <%- getLiteralProp(literal, 'name') %> = <%- value %><% if (i < literals.length - 1) { %>,<% } %>
34
34
  <% } -%>
@@ -49,13 +49,7 @@ if (superTypes && superTypes.length > 0) {
49
49
  const op = genOp.ecoreOperation;
50
50
  const opName = op.getName();
51
51
  const returnType = op.getEType() ? typeMapper.mapClassifier(op.getEType()) : 'void';
52
- const params = [];
53
- for (const param of op.getEParameters()) {
54
- const paramName = param.getName();
55
- const paramType = typeMapper.mapClassifier(param.getEType());
56
- params.push(`${paramName}: ${paramType}`);
57
- }
58
52
  -%>
59
- <%- opName %>(<%- params.join(', ') %>): <%- returnType %>;
53
+ <%- opName %>(<%- mapParams(op) %>): <%- returnType %>;
60
54
  <% } -%>
61
55
  }
@@ -2,12 +2,13 @@
2
2
 
3
3
  import 'reflect-metadata';
4
4
 
5
- // Metadata keys
6
- const MODEL_CLASS_KEY = Symbol('modelClass');
7
- const ATTRIBUTE_KEY = Symbol('attribute');
8
- const REFERENCE_KEY = Symbol('reference');
9
- const ENUM_KEY = Symbol('enum');
10
- const DOCUMENTATION_KEY = Symbol('documentation');
5
+ // Metadata keys: Symbol.for() so every generated copy of this file shares
6
+ // the same identities and cross-package metadata readers see all models (#26)
7
+ const MODEL_CLASS_KEY = Symbol.for('emfts.modelClass');
8
+ const ATTRIBUTE_KEY = Symbol.for('emfts.attribute');
9
+ const REFERENCE_KEY = Symbol.for('emfts.reference');
10
+ const ENUM_KEY = Symbol.for('emfts.enum');
11
+ const DOCUMENTATION_KEY = Symbol.for('emfts.documentation');
11
12
 
12
13
  /**
13
14
  * Decorator for model classes
@@ -2,6 +2,8 @@
2
2
 
3
3
  // Re-export all types from this package
4
4
  <% for (const exp of exports) { -%>
5
- export * from './<%- exp %>';
5
+ export * from './<%- exp %>.js';
6
+ <% } -%>
7
+ <% if (!annotationsPackage) { -%>
8
+ export * from './ModelAnnotations.js';
6
9
  <% } -%>
7
- export * from './ModelAnnotations';
@@ -4,9 +4,43 @@
4
4
  const imports = importResolver.resolveImportsForClass(eClass, genPackage, 'emf');
5
5
  const hardcodedCoreTypes = ['BasicEObject', 'EClass', 'EStructuralFeature'];
6
6
  const coreTypeImports = ['EClass', 'EStructuralFeature'];
7
+ const coreValueImports = [];
7
8
  const typeOnlyImports = [];
8
9
  const valueImports = [];
9
10
  const importedNames = new Set();
11
+ // Check if any feature is many-valued (needs EList imports)
12
+ const useEList = typeMapper.useEList;
13
+ const hasManyFeature = features.some(gf => isMany(gf.ecoreFeature));
14
+ const hasManyMixin = (mixinFeatures || []).some(mf => isMany(mf.feature));
15
+ if (useEList && (hasManyFeature || hasManyMixin)) {
16
+ coreTypeImports.push('EList', 'EReference');
17
+ // Determine which factory functions are needed
18
+ const needsContainment = features.some(gf => isMany(gf.ecoreFeature) && isContainment(gf.ecoreFeature))
19
+ || (mixinFeatures || []).some(mf => isMany(mf.feature) && isContainment(mf.feature));
20
+ const needsEObjectList = features.some(gf => isMany(gf.ecoreFeature) && !isContainment(gf.ecoreFeature) && typeMapper.isReference(gf.ecoreFeature))
21
+ || (mixinFeatures || []).some(mf => isMany(mf.feature) && !isContainment(mf.feature) && typeMapper.isReference(mf.feature));
22
+ const needsBasicList = features.some(gf => isMany(gf.ecoreFeature) && typeMapper.isAttribute(gf.ecoreFeature))
23
+ || (mixinFeatures || []).some(mf => isMany(mf.feature) && typeMapper.isAttribute(mf.feature));
24
+ if (needsContainment) coreValueImports.push('createContainmentEList');
25
+ if (needsEObjectList) coreValueImports.push('createEObjectEList');
26
+ if (needsBasicList) coreValueImports.push('createBasicEList');
27
+ }
28
+ // A type that appears in a value position of a field initializer needs a
29
+ // value import - `import type` is erased and `new T()` would throw at
30
+ // runtime (#35). Value positions: enum member access and instantiation
31
+ const allFeatures = [
32
+ ...features.map(gf => gf.ecoreFeature),
33
+ ...(mixinFeatures || []).map(mf => mf.feature)
34
+ ];
35
+ function isUsedAsValue(typeName) {
36
+ return allFeatures.some(f => {
37
+ const dv = typeMapper.getDefaultValue(f);
38
+ if (dv === null || typeof dv !== 'string') return false;
39
+ return dv.startsWith(typeName + '.')
40
+ || dv.startsWith('new ' + typeName + '(')
41
+ || dv.startsWith('new ' + typeName + '<');
42
+ });
43
+ }
10
44
  for (const imp of imports) {
11
45
  if (imp.typeName === className || imp.typeName === interfaceName || imp.typeName === extendsClass) continue;
12
46
  if (hardcodedCoreTypes.includes(imp.typeName)) continue;
@@ -16,12 +50,7 @@ for (const imp of imports) {
16
50
  }
17
51
  continue;
18
52
  }
19
- // Check if this type is used as a runtime value (e.g., enum default values)
20
- const usedAsValue = features.some(gf => {
21
- const dv = typeMapper.getDefaultValue(gf.ecoreFeature);
22
- return dv !== null && typeof dv === 'string' && dv.startsWith(imp.typeName + '.');
23
- });
24
- if (usedAsValue) {
53
+ if (isUsedAsValue(imp.typeName)) {
25
54
  valueImports.push(imp);
26
55
  } else {
27
56
  typeOnlyImports.push(imp);
@@ -39,13 +68,20 @@ for (const imp of (mixinImports || [])) {
39
68
  }
40
69
  continue;
41
70
  }
42
- typeOnlyImports.push(imp);
71
+ if (isUsedAsValue(imp.typeName)) {
72
+ valueImports.push(imp);
73
+ } else {
74
+ typeOnlyImports.push(imp);
75
+ }
43
76
  importedNames.add(imp.typeName);
44
77
  }
45
78
  -%>
46
79
  <% if (!extendsImport) { -%>
47
80
  import { BasicEObject } from '@emfts/core';
48
81
  <% } -%>
82
+ <% if (coreValueImports.length > 0) { -%>
83
+ import { <%- coreValueImports.join(', ') %> } from '@emfts/core';
84
+ <% } -%>
49
85
  import type { <%- coreTypeImports.join(', ') %> } from '@emfts/core';
50
86
  <% for (const imp of typeOnlyImports) { -%>
51
87
  import type { <%- imp.typeName %> } from '<%- imp.importPath %>';
@@ -56,8 +92,8 @@ import { <%- imp.typeName %> } from '<%- imp.importPath %>';
56
92
  <% if (extendsImport) { -%>
57
93
  import { <%- extendsClass %> } from '<%- extendsImport %>';
58
94
  <% } -%>
59
- import type { <%- interfaceName %> } from './<%- interfaceName %>';
60
- import { <%- packagePrefix %>Package } from './<%- packagePrefix %>Package';
95
+ import type { <%- interfaceName %> } from './<%- interfaceName %>.js';
96
+ import { <%- packagePrefix %>Package } from './<%- packagePrefix %>Package.js';
61
97
 
62
98
  /**
63
99
  * Implementation of <%- interfaceName %>
@@ -80,14 +116,14 @@ export <% if (isAbstract) { %>abstract <% } %>class <%- className %> extends <%-
80
116
  const defaultValue = typeMapper.getDefaultValue(feature);
81
117
  const featureIsMany = isMany(feature);
82
118
  -%>
83
- private _<%- featureName %><% if (!featureIsMany && defaultValue === null) { %>?<% } %>: <%- featureType %><% if (defaultValue !== null) { %> = <%- defaultValue %><% } %>;
119
+ private _<%- featureName %><% if (useEList && featureIsMany) { %>!<% } else if (!featureIsMany && defaultValue === null) { %>?<% } %>: <%- featureType %><% if (defaultValue !== null) { %> = <%- defaultValue %><% } %>;
84
120
  <% } -%>
85
121
  <% for (const mf of (mixinFeatures || [])) {
86
122
  const featureType = typeMapper.mapFeature(mf.feature);
87
123
  const defaultValue = typeMapper.getDefaultValue(mf.feature);
88
124
  const featureIsMany = isMany(mf.feature);
89
125
  -%>
90
- private _<%- mf.name %><% if (!featureIsMany && defaultValue === null) { %>?<% } %>: <%- featureType %><% if (defaultValue !== null) { %> = <%- defaultValue %><% } %>;
126
+ private _<%- mf.name %><% if (useEList && featureIsMany) { %>!<% } else if (!featureIsMany && defaultValue === null) { %>?<% } %>: <%- featureType %><% if (defaultValue !== null) { %> = <%- defaultValue %><% } %>;
91
127
  <% } -%>
92
128
 
93
129
  /**
@@ -104,7 +140,28 @@ export <% if (isAbstract) { %>abstract <% } %>class <%- className %> extends <%-
104
140
  const featureType = typeMapper.mapFeature(feature);
105
141
  const featureIsMany = isMany(feature);
106
142
  const upperName = toUpperSnake(featureName);
143
+ -%>
144
+ <% if (useEList && featureIsMany) {
145
+ // EList lazy-init getter: creates the right EList on first access
146
+ const isRef = typeMapper.isReference(feature);
147
+ const isCont = isRef && isContainment(feature);
148
+ let factoryCall;
149
+ if (isCont) {
150
+ factoryCall = `createContainmentEList<any>(this, this.eClass().getEStructuralFeature('${featureName}') as EReference)`;
151
+ } else if (isRef) {
152
+ factoryCall = `createEObjectEList(this, this.eClass().getEStructuralFeature('${featureName}') as EReference) as unknown as ${featureType}`;
153
+ } else {
154
+ factoryCall = `createBasicEList<any>(this, this.eClass().getEStructuralFeature('${featureName}')!)`;
155
+ }
107
156
  -%>
157
+ get <%- featureName %>(): <%- featureType %> {
158
+ if (!this._<%- featureName %>) {
159
+ this._<%- featureName %> = <%- factoryCall %>;
160
+ }
161
+ return this._<%- featureName %>;
162
+ }
163
+
164
+ <% } else { -%>
108
165
  get <%- featureName %>(): <%- featureType %> {
109
166
  return this._<%- featureName %><% if (!featureIsMany) { %>!<% } %>;
110
167
  }
@@ -134,6 +191,7 @@ export <% if (isAbstract) { %>abstract <% } %>class <%- className %> extends <%-
134
191
  <% } -%>
135
192
  }
136
193
 
194
+ <% } -%>
137
195
  <% } -%>
138
196
  <% } -%>
139
197
  <% for (const mf of (mixinFeatures || [])) {
@@ -177,18 +235,32 @@ export <% if (isAbstract) { %>abstract <% } %>class <%- className %> extends <%-
177
235
  const featureID = this.eClass().getFeatureID(feature);
178
236
  switch (featureID) {
179
237
  <% for (const fid of featureIds) {
180
- const featureType = typeMapper.mapFeature(fid.feature.ecoreFeature);
238
+ const feat = fid.feature.ecoreFeature;
239
+ const featureType = typeMapper.mapFeature(feat);
240
+ const featureName = getName(feat);
241
+ const featureIsMany = isMany(feat);
181
242
  -%>
182
243
  case <%- className %>.<%- fid.name %>:
183
- this.<%- getName(fid.feature.ecoreFeature) %> = newValue as <%- featureType %>;
244
+ <% if (useEList && featureIsMany) { -%>
245
+ this.<%- featureName %>.clear();
246
+ this.<%- featureName %>.addAll(newValue as any[]);
247
+ <% } else { -%>
248
+ this.<%- featureName %> = newValue as <%- featureType %>;
249
+ <% } -%>
184
250
  super.eSet(feature, newValue);
185
251
  break;
186
252
  <% } -%>
187
253
  <% for (const mf of (mixinFeatures || [])) {
188
254
  const featureType = typeMapper.mapFeature(mf.feature);
255
+ const featureIsMany = isMany(mf.feature);
189
256
  -%>
190
257
  case <%- className %>.<%- mf.upperName %>:
258
+ <% if (useEList && featureIsMany) { -%>
259
+ this.<%- mf.name %>.clear();
260
+ this.<%- mf.name %>.addAll(newValue as any[]);
261
+ <% } else { -%>
191
262
  this.<%- mf.name %> = newValue as <%- featureType %>;
263
+ <% } -%>
192
264
  super.eSet(feature, newValue);
193
265
  break;
194
266
  <% } -%>
@@ -210,7 +282,9 @@ export <% if (isAbstract) { %>abstract <% } %>class <%- className %> extends <%-
210
282
  const featureName = getName(feature);
211
283
  -%>
212
284
  case <%- className %>.<%- fid.name %>:
213
- <% if (featureIsMany) { -%>
285
+ <% if (useEList && featureIsMany) { -%>
286
+ return this._<%- featureName %> !== undefined && !this._<%- featureName %>.isEmpty();
287
+ <% } else if (featureIsMany) { -%>
214
288
  return this._<%- featureName %> !== undefined && this._<%- featureName %>.length > 0;
215
289
  <% } else if (defaultValue !== null) { -%>
216
290
  return this._<%- featureName %> !== <%- defaultValue %>;
@@ -223,7 +297,9 @@ export <% if (isAbstract) { %>abstract <% } %>class <%- className %> extends <%-
223
297
  const defaultValue = typeMapper.getDefaultValue(mf.feature);
224
298
  -%>
225
299
  case <%- className %>.<%- mf.upperName %>:
226
- <% if (featureIsMany) { -%>
300
+ <% if (useEList && featureIsMany) { -%>
301
+ return this._<%- mf.name %> !== undefined && !this._<%- mf.name %>.isEmpty();
302
+ <% } else if (featureIsMany) { -%>
227
303
  return this._<%- mf.name %> !== undefined && this._<%- mf.name %>.length > 0;
228
304
  <% } else if (defaultValue !== null) { -%>
229
305
  return this._<%- mf.name %> !== <%- defaultValue %>;
@@ -249,7 +325,9 @@ export <% if (isAbstract) { %>abstract <% } %>class <%- className %> extends <%-
249
325
  const featureName = getName(feature);
250
326
  -%>
251
327
  case <%- className %>.<%- fid.name %>:
252
- <% if (featureIsMany) { -%>
328
+ <% if (useEList && featureIsMany) { -%>
329
+ if (this._<%- featureName %>) this._<%- featureName %>.clear();
330
+ <% } else if (featureIsMany) { -%>
253
331
  this._<%- featureName %> = [];
254
332
  <% } else if (defaultValue !== null) { -%>
255
333
  this._<%- featureName %> = <%- defaultValue %>;
@@ -263,7 +341,9 @@ export <% if (isAbstract) { %>abstract <% } %>class <%- className %> extends <%-
263
341
  const defaultValue = typeMapper.getDefaultValue(mf.feature);
264
342
  -%>
265
343
  case <%- className %>.<%- mf.upperName %>:
266
- <% if (featureIsMany) { -%>
344
+ <% if (useEList && featureIsMany) { -%>
345
+ if (this._<%- mf.name %>) this._<%- mf.name %>.clear();
346
+ <% } else if (featureIsMany) { -%>
267
347
  this._<%- mf.name %> = [];
268
348
  <% } else if (defaultValue !== null) { -%>
269
349
  this._<%- mf.name %> = <%- defaultValue %>;
@@ -2,13 +2,13 @@
2
2
 
3
3
  import { BasicEFactory } from '@emfts/core';
4
4
  import type { EClass, EObject } from '@emfts/core';
5
- import { <%- packageName %>Package } from './<%- packageName %>Package';
5
+ import { <%- packageName %>Package } from './<%- packageName %>Package.js';
6
6
  <% for (const genClass of concreteClasses) {
7
7
  const className = genClass.ecoreClass.getName();
8
8
  const implName = genClass.implClassName || (className + 'Impl');
9
9
  -%>
10
- import type { <%- className %> } from './<%- className %>';
11
- import { <%- implName %> } from './<%- implName %>';
10
+ import type { <%- className %> } from './<%- className %>.js';
11
+ import { <%- implName %> } from './<%- implName %>.js';
12
12
  <% } -%>
13
13
 
14
14
  /**
@@ -1,29 +1,35 @@
1
1
  <%- header %>
2
2
 
3
- // Package and Factory
4
- export { <%- packageName %>Package } from './<%- packageName %>Package';
5
- export { <%- packageName %>Factory } from './<%- packageName %>Factory';
3
+ // Package<% if (generateFactory) { %> and Factory<% } %>
4
+ export { <%- packageName %>Package } from './<%- packageName %>Package.js';
5
+ <% if (generateFactory) { -%>
6
+ export { <%- packageName %>Factory } from './<%- packageName %>Factory.js';
7
+ <% } -%>
6
8
 
9
+ <% if (generateInterfaces) { -%>
7
10
  // Interfaces
8
11
  <% for (const genClass of genClasses) {
9
12
  const name = getName(genClass.ecoreClass);
10
13
  -%>
11
- export type { <%- name %> } from './<%- name %>';
14
+ export type { <%- name %> } from './<%- name %>.js';
15
+ <% } -%>
12
16
  <% } -%>
13
17
 
18
+ <% if (generateClasses) { -%>
14
19
  // Implementations (including abstract classes, as they're extended by subclasses)
15
20
  <% for (const genClass of genClasses) {
16
21
  if (!checkIsInterface(genClass.ecoreClass)) {
17
22
  const implName = genClass.implClassName || (getName(genClass.ecoreClass) + 'Impl');
18
23
  -%>
19
- export { <%- implName %> } from './<%- implName %>';
24
+ export { <%- implName %> } from './<%- implName %>.js';
20
25
  <%
21
26
  }
22
27
  } -%>
28
+ <% } -%>
23
29
 
24
30
  // Enums
25
31
  <% for (const genEnum of genEnums) {
26
32
  const name = getName(genEnum.ecoreEnum);
27
33
  -%>
28
- export { <%- name %>, get<%- name %>, get<%- name %>ByLiteral } from './<%- name %>';
34
+ export { <%- name %>, get<%- name %>, get<%- name %>ByLiteral } from './<%- name %>.js';
29
35
  <% } -%>
@@ -4,6 +4,11 @@
4
4
  const imports = importResolver.resolveImportsForClass(eClass, genPackage, 'emf');
5
5
  const coreTypeImports = ['EObject'];
6
6
  const otherTypeImports = [];
7
+ // Add EList import if any feature is many-valued and useEList is on
8
+ const useEList = typeMapper.useEList;
9
+ if (useEList && features.some(gf => isMany(gf.ecoreFeature))) {
10
+ coreTypeImports.push('EList');
11
+ }
7
12
  for (const imp of imports) {
8
13
  if (imp.typeName === interfaceName) continue;
9
14
  if (imp.importPath === '@emfts/core') {
@@ -62,7 +62,7 @@ function typeRefExpr(classifier, castTo) {
62
62
  // External dependency package — resolve via EPackageRegistry
63
63
  const depNsURI = getClassifierNsURI(classifier);
64
64
  if (depNsURI) {
65
- return "EPackageRegistry.INSTANCE.getEPackage('" + depNsURI + "')!.getEClassifier('" + typeName + "')!" + cast;
65
+ return "requireEPackage('" + depNsURI + "').getEClassifier('" + typeName + "')!" + cast;
66
66
  }
67
67
  return "getEcorePackage().getEClassifier('" + typeName + "')!" + cast;
68
68
  }
@@ -80,7 +80,7 @@ for (const genClass of genClasses) {
80
80
  }
81
81
  for (const genFeature of genClass.genFeatures) {
82
82
  const eType = getEType(genFeature.ecoreFeature);
83
- if (eType && typeMapper.isReference(genFeature.ecoreFeature) && !isLocalClassifier(eType)) {
83
+ if (eType && !isLocalClassifier(eType)) {
84
84
  if (isEcoreClassifier(eType)) { needsEcoreImport = true; }
85
85
  else { needsRegistryImport = true; }
86
86
  }
@@ -88,12 +88,30 @@ for (const genClass of genClasses) {
88
88
  }
89
89
  -%>
90
90
  <%
91
- const coreImports = ['BasicEPackage', 'BasicEClass', 'BasicEAttribute', 'BasicEReference'];
91
+ // EPackageRegistry is always needed: the package registers itself under its
92
+ // nsURI so that other generated packages can resolve their references (#36)
93
+ const coreImports = ['BasicEPackage', 'BasicEClass', 'BasicEAttribute', 'BasicEReference', 'EPackageRegistry'];
92
94
  if (needsEcoreImport) coreImports.push('getEcorePackage');
93
- if (needsRegistryImport) coreImports.push('EPackageRegistry');
94
95
  -%>
95
96
  import { <%- coreImports.join(', ') %> } from '@emfts/core';
96
97
  import type { EClass, EAttribute, EReference, EEnum } from '@emfts/core';
98
+ <% if (generateFactory) { -%>
99
+ import { <%- packageName %>Factory } from './<%- packageName %>Factory.js';
100
+ <% } -%>
101
+ <% if (needsRegistryImport) { -%>
102
+
103
+ /**
104
+ * Resolve a dependency package from the registry, failing with an actionable
105
+ * message instead of a null dereference when it is not registered yet
106
+ */
107
+ function requireEPackage(nsURI: string) {
108
+ const pkg = EPackageRegistry.INSTANCE.getEPackage(nsURI);
109
+ if (!pkg) {
110
+ throw new Error(`EPackage '${nsURI}' is not registered. Access the eINSTANCE of that model's generated package (or register it via EPackageRegistry.INSTANCE.registerPackage) before initializing <%- packageName %>Package.`);
111
+ }
112
+ return pkg;
113
+ }
114
+ <% } -%>
97
115
 
98
116
  /**
99
117
  * <%- packageName %> Package
@@ -138,6 +156,15 @@ export class <%- packageName %>Package extends BasicEPackage {
138
156
  * Initialize package contents
139
157
  */
140
158
  private init(): void {
159
+ // Register this package under its nsURI so other generated packages can
160
+ // resolve their cross-package references from the registry (#36).
161
+ // eINSTANCE is already assigned at this point
162
+ EPackageRegistry.INSTANCE.set(<%- packageName %>Package.eNS_URI, this);
163
+ <% if (generateFactory) { -%>
164
+ // Wire the generated factory so loaded/created instances are typed Impls.
165
+ this.setEFactoryInstance(<%- packageName %>Factory.eINSTANCE);
166
+ <% } -%>
167
+
141
168
  <% for (const genClass of genClasses) {
142
169
  const eClass = genClass.ecoreClass;
143
170
  const className = getName(eClass);
@@ -195,7 +222,9 @@ export class <%- packageName %>Package extends BasicEPackage {
195
222
  } -%>
196
223
 
197
224
  // ============================================
198
- // Set ETypes for EReferences (must be done after all classes are created)
225
+ // Set ETypes for all features (must be done after all classes are created).
226
+ // An EAttribute without eType leaves the XMI reader no EDataType to
227
+ // convert against - every value would arrive as a raw string (#37)
199
228
  // ============================================
200
229
  <% for (const genClass of genClasses) {
201
230
  const eClass = genClass.ecoreClass;
@@ -206,14 +235,15 @@ export class <%- packageName %>Package extends BasicEPackage {
206
235
  const featureName = getName(feature);
207
236
  const featureUpperName = featureName.replace(/([A-Z])/g, '_$1').toUpperCase().replace(/^_/, '');
208
237
  const isRef = typeMapper.isReference(feature);
209
- if (isRef) {
210
- const eType = getEType(feature);
211
- if (eType) {
238
+ const eType = getEType(feature);
239
+ if (!eType) continue;
240
+ // Local enums and datatypes have no initialized Literals entry yet
241
+ // (see the enum TODO above) - only class literals exist locally
242
+ if (!isRef && isLocalClassifier(eType)) continue;
243
+ const castType = isRef ? 'BasicEReference' : 'BasicEAttribute';
212
244
  -%>
213
- (<%- packageName %>Package.Literals.<%- upperName %>__<%- featureUpperName %> as BasicEReference).setEType(<%- typeRefExpr(eType) %>);
214
- <% }
215
- }
216
- }
245
+ (<%- packageName %>Package.Literals.<%- upperName %>__<%- featureUpperName %> as <%- castType %>).setEType(<%- typeRefExpr(eType) %>);
246
+ <% }
217
247
  } -%>
218
248
 
219
249
  // ============================================
@@ -60,14 +60,8 @@ if (superTypes && superTypes.length > 0) {
60
60
  const op = genOp.ecoreOperation;
61
61
  const opName = op.getName();
62
62
  const returnType = op.getEType() ? typeMapper.mapClassifier(op.getEType()) : 'void';
63
- const params = [];
64
- for (const param of op.getEParameters()) {
65
- const paramName = param.getName();
66
- const paramType = typeMapper.mapClassifier(param.getEType());
67
- params.push(`${paramName}: ${paramType}`);
68
- }
69
63
  -%>
70
- <%- opName %>(<%- params.join(', ') %>): <%- returnType %> {
64
+ <%- opName %>(<%- mapParams(op) %>): <%- returnType %> {
71
65
  throw new Error('<%- opName %> not implemented');
72
66
  }
73
67