@typemf/generator 0.6.0 → 0.8.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/dist/cli.js +67 -17
- package/dist/index.d.ts +29 -32
- package/dist/index.js +69 -17
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/templates/typescript/eclass.njk +25 -46
- package/templates/typescript/efactory.njk +9 -0
- package/templates/typescript/epackage.njk +29 -51
- package/templates/typescript/epackageref.njk +25 -27
- package/templates/typescript/etypeguards.njk +3 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typemf/generator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"provenance": true
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@typemf/core": "0.
|
|
34
|
-
"@typemf/node": "0.
|
|
35
|
-
"@typemf/xmi": "0.
|
|
33
|
+
"@typemf/core": "0.8.0",
|
|
34
|
+
"@typemf/node": "0.8.0",
|
|
35
|
+
"@typemf/xmi": "0.8.0",
|
|
36
36
|
"nunjucks": "^3.2.4",
|
|
37
37
|
"prettier": "^3.9.8"
|
|
38
38
|
},
|
|
@@ -43,7 +43,7 @@ export interface {{ eClass.getName() }} extends {{ superClass.getName() if super
|
|
|
43
43
|
{% else %}
|
|
44
44
|
{% set _ = useImport("EObjectImpl", "impl", {foundational: true}) %}
|
|
45
45
|
{% endif %}
|
|
46
|
-
{% set _ = useImport("BasicEList", "impl", {foundational: true}) %}
|
|
46
|
+
{% if options['generate-ecore'] or hasStoredManyValuedFeature(ownFeatures) %}{% set _ = useImport("BasicEList", "impl", {foundational: true}) %}{% endif %}
|
|
47
47
|
{% for feature in ownFeatures %}
|
|
48
48
|
{% if feature.isMany() %}
|
|
49
49
|
{% set _ = useImport("EList", "types", {foundational: true}) %}
|
|
@@ -115,72 +115,54 @@ export class {{ eClass.getName() }}Impl extends {{ (superClass.getName() + "Impl
|
|
|
115
115
|
{% endif %}
|
|
116
116
|
{% endfor %}
|
|
117
117
|
|
|
118
|
-
{% if eClass.getName() == "EClassifier" %}
|
|
119
|
-
{#-
|
|
120
|
-
|
|
121
|
-
Ecore feature - so it's declared here, once, on EClassifier itself
|
|
122
|
-
(inherited by every descendant: EClass, EDataType, EEnum, ...)
|
|
123
|
-
rather than emitted by the ordinary feature-driven generation
|
|
124
|
-
above, which has no feature to generate it from. Confirmed
|
|
125
|
-
necessary by actually running self-hosted bootstrap code (which
|
|
126
|
-
constructs real EClassifier-derived metaclass shells and needs to
|
|
127
|
-
set their classifierID), not assumed - see NOTES.md. #}
|
|
118
|
+
{% if options['generate-ecore'] and eClass.getName() == "EClassifier" %}
|
|
119
|
+
{#- Ecore only: the classifier ID is bookkeeping, not a modeled feature, inherited by every
|
|
120
|
+
classifier. #}
|
|
128
121
|
private classifierId = -1;
|
|
129
122
|
|
|
130
123
|
getClassifierID(): number {
|
|
131
124
|
return this.classifierId;
|
|
132
125
|
}
|
|
133
126
|
|
|
127
|
+
/** Sets the classifier's ID; called by generated packages. */
|
|
134
128
|
setClassifierID(id: number): void {
|
|
135
129
|
this.classifierId = id;
|
|
136
130
|
}
|
|
137
131
|
{% endif %}
|
|
138
|
-
{% if eClass.getName() == "EStructuralFeature" %}
|
|
139
|
-
{#-
|
|
140
|
-
|
|
141
|
-
@typemf/core's own hand-written EStructuralFeatureImpl), not a
|
|
142
|
-
modeled Ecore feature, declared once here on EStructuralFeature
|
|
143
|
-
itself (inherited by EAttribute, EReference, ...). Found the same
|
|
144
|
-
way: running self-hosted bootstrap code, which constructs real
|
|
145
|
-
EStructuralFeature-derived metaclass shells (EAttributeImpl,
|
|
146
|
-
EReferenceImpl) and needs to set their featureID - see NOTES.md. #}
|
|
132
|
+
{% if options['generate-ecore'] and eClass.getName() == "EStructuralFeature" %}
|
|
133
|
+
{#- Ecore only: the feature ID (bookkeeping) and the container class (getter modeled as an
|
|
134
|
+
operation, setter only on the implementation, used during bootstrap). #}
|
|
147
135
|
private featureId = -1;
|
|
148
136
|
|
|
149
137
|
getFeatureID(): number {
|
|
150
138
|
return this.featureId;
|
|
151
139
|
}
|
|
152
140
|
|
|
141
|
+
/** Sets the feature's ID; called by generated packages. */
|
|
153
142
|
setFeatureID(id: number): void {
|
|
154
143
|
this.featureId = id;
|
|
155
144
|
}
|
|
156
|
-
|
|
157
|
-
doc comment in @typemf/core) - PUBLIC getter (a real, modeled, reflectively-callable operation),
|
|
158
|
-
IMPL-ONLY setter (never on the interface, set once during bootstrap - see epackage.njk). Not
|
|
159
|
-
internal bookkeeping like featureID (it IS a real declared operation), but shares the same
|
|
160
|
-
"special-cased field the normal per-operation loop must skip" shape, so isBookkeepingOperation
|
|
161
|
-
treats it the same way (see typescript-filters.ts). #}
|
|
145
|
+
|
|
162
146
|
private containerClass: {{ useImport("TypeScriptClass", "types", {foundational: true}) }}<unknown> | undefined;
|
|
163
147
|
|
|
164
148
|
getContainerClass(): {{ useImport("TypeScriptClass", "types", {foundational: true}) }}<unknown> | undefined {
|
|
165
149
|
return this.containerClass;
|
|
166
150
|
}
|
|
167
151
|
|
|
152
|
+
/** Sets the implementation class of the declaring class; called by generated packages. */
|
|
168
153
|
setContainerClass(cls: {{ useImport("TypeScriptClass", "types", {foundational: true}) }}<unknown> | undefined): void {
|
|
169
154
|
this.containerClass = cls;
|
|
170
155
|
}
|
|
171
156
|
{% endif %}
|
|
172
|
-
{% if eClass.getName() == "EOperation" %}
|
|
173
|
-
{#-
|
|
174
|
-
bookkeeping (matching @typemf/core's own hand-written EOperationImpl), not a modeled Ecore
|
|
175
|
-
feature, declared once here on EOperation itself. Same discovery path: self-hosted bootstrap
|
|
176
|
-
code constructs real EOperationImpl instances and needs to set their operationID - see NOTES.md
|
|
177
|
-
(point 4). #}
|
|
157
|
+
{% if options['generate-ecore'] and eClass.getName() == "EOperation" %}
|
|
158
|
+
{#- Ecore only: the operation ID is bookkeeping, not a modeled feature. #}
|
|
178
159
|
private operationId = -1;
|
|
179
160
|
|
|
180
161
|
getOperationID(): number {
|
|
181
162
|
return this.operationId;
|
|
182
163
|
}
|
|
183
164
|
|
|
165
|
+
/** Sets the operation's ID; called by generated packages. */
|
|
184
166
|
setOperationID(id: number): void {
|
|
185
167
|
this.operationId = id;
|
|
186
168
|
}
|
|
@@ -196,11 +178,12 @@ export class {{ eClass.getName() }}Impl extends {{ (superClass.getName() + "Impl
|
|
|
196
178
|
{% if feature.isMany() %}
|
|
197
179
|
{% if getter %}
|
|
198
180
|
{% set _featureImports = registerBodyImports(feature) %}
|
|
181
|
+
{#- Computed: the cached list is read-only, since changing it would never reach the model. #}
|
|
199
182
|
get{{ ucfirst(feature.getName()) }}(): EList<{{ tsScalarType(feature) }}> {
|
|
200
183
|
if (this._{{ feature.getName() }}CacheGeneration !== EObjectImpl.getModelGeneration()) {
|
|
201
|
-
this._{{ feature.getName() }}Cache = (() => {
|
|
184
|
+
this._{{ feature.getName() }}Cache = new {{ useImport("UnmodifiableEList", "impl", {foundational: true}) }}((() => {
|
|
202
185
|
{{ getter | indent(8) }}
|
|
203
|
-
})();
|
|
186
|
+
})());
|
|
204
187
|
this._{{ feature.getName() }}CacheGeneration = EObjectImpl.getModelGeneration();
|
|
205
188
|
}
|
|
206
189
|
return this._{{ feature.getName() }}Cache!;
|
|
@@ -339,6 +322,7 @@ export class {{ eClass.getName() }}Impl extends {{ (superClass.getName() + "Impl
|
|
|
339
322
|
{% endfor %}
|
|
340
323
|
|
|
341
324
|
eGet(feature: EStructuralFeature): unknown {
|
|
325
|
+
this.requireOwnFeature(feature);
|
|
342
326
|
switch (feature.getFeatureID()) {
|
|
343
327
|
{% for feature in ownFeatures %}
|
|
344
328
|
{% if featureGetter(feature) or trivialDerivedFormula(feature) %}
|
|
@@ -363,6 +347,7 @@ export class {{ eClass.getName() }}Impl extends {{ (superClass.getName() + "Impl
|
|
|
363
347
|
}
|
|
364
348
|
|
|
365
349
|
eSet(feature: EStructuralFeature, value: unknown): void {
|
|
350
|
+
this.requireOwnFeature(feature);
|
|
366
351
|
switch (feature.getFeatureID()) {
|
|
367
352
|
{% for feature in ownFeatures %}
|
|
368
353
|
{% if feature.isMany() %}
|
|
@@ -445,18 +430,9 @@ export class {{ eClass.getName() }}Impl extends {{ (superClass.getName() + "Impl
|
|
|
445
430
|
}
|
|
446
431
|
}
|
|
447
432
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
exists to back the ordinary getter's lazy construction. Constructs
|
|
452
|
-
the cache with no feature reference when called this way - a real,
|
|
453
|
-
narrow, deliberate limitation: containment/opposite maintenance on
|
|
454
|
-
a list built this way is unavailable, since there's no feature to
|
|
455
|
-
derive it from. Acceptable specifically because only the metamodel's
|
|
456
|
-
own self-describing bootstrap instances (EClassImpl, EAttributeImpl,
|
|
457
|
-
etc., as constructed by {Pkg}PackageImpl) ever take this path -
|
|
458
|
-
ordinary domain instances always go through the normal, fully-
|
|
459
|
-
wired getter instead, never this one. #}
|
|
433
|
+
{% if options['generate-ecore'] %}
|
|
434
|
+
{#- Ecore only: the bootstrap adds to lists before the metaclasses their getters need exist. #}
|
|
435
|
+
/** @internal The list of the many-valued feature `featureId`, for bootstrapping the package. */
|
|
460
436
|
eBootstrapList(featureId: number): BasicEList<unknown> {
|
|
461
437
|
switch (featureId) {
|
|
462
438
|
{% for feature in ownFeatures %}
|
|
@@ -474,8 +450,10 @@ export class {{ eClass.getName() }}Impl extends {{ (superClass.getName() + "Impl
|
|
|
474
450
|
{% endif %}
|
|
475
451
|
}
|
|
476
452
|
}
|
|
453
|
+
{% endif %}
|
|
477
454
|
|
|
478
455
|
eIsSet(feature: EStructuralFeature): boolean {
|
|
456
|
+
this.requireOwnFeature(feature);
|
|
479
457
|
switch (feature.getFeatureID()) {
|
|
480
458
|
{% for feature in ownFeatures %}
|
|
481
459
|
{% if feature.isMany() %}
|
|
@@ -526,6 +504,7 @@ export class {{ eClass.getName() }}Impl extends {{ (superClass.getName() + "Impl
|
|
|
526
504
|
}
|
|
527
505
|
|
|
528
506
|
eUnset(feature: EStructuralFeature): void {
|
|
507
|
+
this.requireOwnFeature(feature);
|
|
529
508
|
switch (feature.getFeatureID()) {
|
|
530
509
|
{% for feature in ownFeatures %}
|
|
531
510
|
{% if feature.isMany() and not featureGetter(feature) %}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% set eFactoryClassifier = findEClassByName(package, "EFactory") %}
|
|
2
|
+
{% set pkgClass = packageClassName(package) %}
|
|
2
3
|
{% file factoryClassName(package) + ".ts" %}
|
|
3
4
|
export interface {{ factoryClassName(package) }} extends {{ useImport("EFactory", "types", {foundational: true}) }} {
|
|
4
5
|
{% for classifier in concreteEClassesOf(package) %}
|
|
@@ -25,6 +26,14 @@ export interface {{ factoryClassName(package) }} extends {{ useImport("EFactory"
|
|
|
25
26
|
double-import the old, pre-refactor version of this template had to
|
|
26
27
|
guard against explicitly. #}
|
|
27
28
|
export class {{ factoryClassName(package) }}Impl extends {{ useImport("EFactoryImpl", "impl", {foundational: true}) }} implements {{ useImport(factoryClassName(package), "root", {foundational: false}) }} {
|
|
29
|
+
/**
|
|
30
|
+
* The factory instance, as EMF's `{{ factoryClassName(package) }}.eINSTANCE` (on the class, since
|
|
31
|
+
* TypeScript interfaces have no statics). Reading it constructs the package if necessary.
|
|
32
|
+
*/
|
|
33
|
+
static get eINSTANCE(): {{ factoryClassName(package) }} {
|
|
34
|
+
return {{ useImport("get" + pkgClass + "Ref", "impl", {file: pkgClass + "Ref", foundational: false}) }}().getEFactoryInstance() as {{ factoryClassName(package) }};
|
|
35
|
+
}
|
|
36
|
+
|
|
28
37
|
{% for classifier in concreteEClassesOf(package) %}
|
|
29
38
|
create{{ classifier.getName() }}(): {{ useImport(classifier.getName(), "types", {foundational: false}) }} {
|
|
30
39
|
return new {{ useImport(classifier.getName() + "Impl", "impl", {foundational: false}) }}();
|
|
@@ -4,13 +4,7 @@
|
|
|
4
4
|
{% set _ = useImport(name, "types", {foundational: true}) %}
|
|
5
5
|
{% endfor %}
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* Classifier/feature IDs, as pure numeric literals - safe to import from
|
|
9
|
-
* anywhere (Factory, Switch) with zero risk of the circular-import hazard
|
|
10
|
-
* that made a lazy, explicit init() necessary for the singleton itself
|
|
11
|
-
* (see {{ pkgClass }}Impl.ts) - these are just numbers, never require
|
|
12
|
-
* constructing anything.
|
|
13
|
-
*/
|
|
7
|
+
/** The classifier, feature and operation IDs; plain numbers, safe to import from anywhere. */
|
|
14
8
|
export const Ids = {
|
|
15
9
|
{% for classifier in package.getEClassifiers() %}
|
|
16
10
|
{{ classifier.getName() }}: { self: {{ classifier.getClassifierID() }}{% if isEClass(classifier) %}{% for feature in classifier.getEStructuralFeatures() %}, {{ feature.getName() }}: {{ feature.getFeatureID() }}{% endfor %}{% for op in classifier.getEOperations() %}, op{{ loop.index0 }}: {{ op.getOperationID() }}{% endfor %}{% endif %} },
|
|
@@ -60,27 +54,27 @@ export interface {{ pkgClass }} extends {{ useImport("EPackage", "types", {found
|
|
|
60
54
|
{% set needsEnum = false %}
|
|
61
55
|
{% set needsEnumLiteral = false %}
|
|
62
56
|
{% set needsAnnotation = false %}
|
|
63
|
-
{% if package
|
|
57
|
+
{% if runtimeAnnotations(package).length > 0 %}{% set needsAnnotation = true %}{% endif %}
|
|
64
58
|
{% for classifier in package.getEClassifiers() %}
|
|
65
|
-
{% if classifier
|
|
59
|
+
{% if runtimeAnnotations(classifier).length > 0 %}{% set needsAnnotation = true %}{% endif %}
|
|
66
60
|
{% if isEClass(classifier) %}
|
|
67
61
|
{% for feature in classifier.getEStructuralFeatures() %}
|
|
68
62
|
{% if isEReference(feature) %}{% set needsReference = true %}{% else %}{% set needsAttribute = true %}{% endif %}
|
|
69
|
-
{% if feature
|
|
63
|
+
{% if runtimeAnnotations(feature).length > 0 %}{% set needsAnnotation = true %}{% endif %}
|
|
70
64
|
{% endfor %}
|
|
71
65
|
{% for op in classifier.getEOperations() %}
|
|
72
66
|
{% set needsOperation = true %}
|
|
73
|
-
{% if op
|
|
67
|
+
{% if runtimeAnnotations(op).length > 0 %}{% set needsAnnotation = true %}{% endif %}
|
|
74
68
|
{% if op.getEParameters().size() > 0 %}{% set needsParameter = true %}{% endif %}
|
|
75
69
|
{% for param in op.getEParameters() %}
|
|
76
|
-
{% if param
|
|
70
|
+
{% if runtimeAnnotations(param).length > 0 %}{% set needsAnnotation = true %}{% endif %}
|
|
77
71
|
{% endfor %}
|
|
78
72
|
{% endfor %}
|
|
79
73
|
{% elif isEEnum(classifier) %}
|
|
80
74
|
{% set needsEnum = true %}
|
|
81
75
|
{% for literal in classifier.getELiterals() %}
|
|
82
76
|
{% set needsEnumLiteral = true %}
|
|
83
|
-
{% if literal
|
|
77
|
+
{% if runtimeAnnotations(literal).length > 0 %}{% set needsAnnotation = true %}{% endif %}
|
|
84
78
|
{% endfor %}
|
|
85
79
|
{% else %}
|
|
86
80
|
{% set needsDataType = true %}
|
|
@@ -104,10 +98,12 @@ export interface {{ pkgClass }} extends {{ useImport("EPackage", "types", {found
|
|
|
104
98
|
{% set _ = useImport(factoryClassName(package), "root", {foundational: false}) %}
|
|
105
99
|
{% set _ = useImport(factoryClassName(package) + "Impl", "impl", {foundational: false}) %}
|
|
106
100
|
{% set _ = useImport("set" + pkgClass + "Ref", "impl", {file: pkgClass + "Ref", foundational: false}) %}
|
|
101
|
+
{% set _ = useImport("register" + pkgClass + "Initializer", "impl", {file: pkgClass + "Ref", foundational: false}) %}
|
|
107
102
|
|
|
108
103
|
{#-
|
|
109
|
-
Emits code reconstructing `element`'s
|
|
110
|
-
|
|
104
|
+
Emits code reconstructing `element`'s EAnnotations (source + details) onto
|
|
105
|
+
the runtime object built at `targetExpr`, except the generator-only ones
|
|
106
|
+
(see runtimeAnnotations). Scoped to
|
|
111
107
|
package/classifier/feature/enum-literal only - EOperations have no
|
|
112
108
|
runtime representation in the converted metamodel yet, so there's
|
|
113
109
|
nowhere for an operation-level annotation to attach (see NOTES.md).
|
|
@@ -155,7 +151,7 @@ export interface {{ pkgClass }} extends {{ useImport("EPackage", "types", {found
|
|
|
155
151
|
everywhere else in this file (the metaclass for EStringToStringMapEntry doesn't exist yet at the
|
|
156
152
|
point this code runs during self-construction); the ordinary branch has no such ordering problem and
|
|
157
153
|
uses the real, public API (setDetailValue) directly. #}
|
|
158
|
-
{% for annotation in element
|
|
154
|
+
{% for annotation in runtimeAnnotations(element) %}
|
|
159
155
|
{
|
|
160
156
|
const annotation = new EAnnotationImpl();
|
|
161
157
|
{% if annotation.getSource() %}
|
|
@@ -207,14 +203,9 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
207
203
|
private static _instance: {{ pkgClass }}Impl | undefined;
|
|
208
204
|
|
|
209
205
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* overriding createX() to return your own derived classes). Whichever
|
|
214
|
-
* caller gets here FIRST wins; a later call with a *different* factory
|
|
215
|
-
* is logged and otherwise ignored, rather than silently replacing an
|
|
216
|
-
* already-in-use singleton or throwing on a caller who didn't cause the
|
|
217
|
-
* conflict.
|
|
206
|
+
* Constructs the package, if that hasn't happened yet, and returns it. To use your own factory
|
|
207
|
+
* (e.g. one returning subclasses), call this with it before anything reads `eINSTANCE`; a
|
|
208
|
+
* factory passed after construction is ignored with a warning.
|
|
218
209
|
*/
|
|
219
210
|
static init(factory?: {{ factoryClassName(package) }}): {{ pkgClass }}Impl {
|
|
220
211
|
if (!{{ pkgClass }}Impl._instance) {
|
|
@@ -235,10 +226,7 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
235
226
|
|
|
236
227
|
private constructor() {
|
|
237
228
|
super();
|
|
238
|
-
// Set
|
|
239
|
-
// which may need this reference - see {{ pkgClass }}Ref.ts's own doc
|
|
240
|
-
// comment for why this is a late-bound reference rather than a
|
|
241
|
-
// direct import of {{ pkgClass }}Impl.
|
|
229
|
+
// Set first: constructing the classifiers below already needs the package reference.
|
|
242
230
|
set{{ pkgClass }}Ref(this);
|
|
243
231
|
|
|
244
232
|
// Pass 1: create every classifier (empty shell - no features/supertypes
|
|
@@ -266,10 +254,7 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
266
254
|
{{ bootstrapSet("literal", "setLiteral", "Ids.EEnumLiteral.literal", jsString(literal.getLiteral())) }}
|
|
267
255
|
{{ emitAnnotations("literal", literal) }}
|
|
268
256
|
{% if options['generate-ecore'] %}
|
|
269
|
-
//
|
|
270
|
-
// hand-written convenience method (it's not a reflective feature),
|
|
271
|
-
// so the list-add and the eEnum backpointer (a real, modeled,
|
|
272
|
-
// non-changeable opposite - see NOTES.md) are wired manually.
|
|
257
|
+
// eEnum is not changeable, so both ends of the opposite are set directly.
|
|
273
258
|
this._{{ classifier.getName() }}.eBootstrapList(Ids.EEnum.eLiterals).add(literal);
|
|
274
259
|
literal.eBasicSetValue(Ids.EEnumLiteral.eEnum, this._{{ classifier.getName() }});
|
|
275
260
|
{% else %}
|
|
@@ -325,6 +310,9 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
325
310
|
{{ bootstrapSet(fieldExpr, "setChangeable", "Ids.EStructuralFeature.changeable", feature.isChangeable()) }}
|
|
326
311
|
{{ bootstrapSet(fieldExpr, "setOrdered", "Ids.ETypedElement.ordered", feature.isOrdered()) }}
|
|
327
312
|
{{ bootstrapSet(fieldExpr, "setUnique", "Ids.ETypedElement.unique", feature.isUnique()) }}
|
|
313
|
+
{% if feature.getDefaultValueLiteral() !== undefined %}
|
|
314
|
+
{{ bootstrapSet(fieldExpr, "setDefaultValueLiteral", "Ids.EStructuralFeature.defaultValueLiteral", jsString(feature.getDefaultValueLiteral())) }}
|
|
315
|
+
{% endif %}
|
|
328
316
|
{{ fieldExpr }}.setFeatureID(Ids.{{ classifier.getName() }}.{{ feature.getName() }});
|
|
329
317
|
{#- containerClass: the concrete implementation class the feature is declared on - the closest
|
|
330
318
|
TypeScript has to Java's own containerClass semantics (Class<?>, e.g. EClassifier.class in the
|
|
@@ -345,11 +333,8 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
345
333
|
{% endfor %}
|
|
346
334
|
{% endfor %}
|
|
347
335
|
|
|
348
|
-
// Pass 2.5: operations and their parameters
|
|
349
|
-
//
|
|
350
|
-
// this is what actually populates it, the same way pass 2 populates eStructuralFeatures. Kept as
|
|
351
|
-
// its own pass, after every classifier and feature exists (an operation's return type or a
|
|
352
|
-
// parameter's type can reference either), rather than folded into the loop above.
|
|
336
|
+
// Pass 2.5: operations and their parameters, after every classifier and feature exists, since
|
|
337
|
+
// their types can refer to any of them.
|
|
353
338
|
{% for classifier in eClassesOf(package) %}
|
|
354
339
|
{% for op in classifier.getEOperations() %}
|
|
355
340
|
{% set opIndex = loop.index0 %}
|
|
@@ -406,21 +391,9 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
406
391
|
{% endfor %}
|
|
407
392
|
{% endfor %}
|
|
408
393
|
|
|
409
|
-
// Pass 5 (recompute EAll* caches, ordinary mode only) removed - stale post-swap.
|
|
410
|
-
// @typemf/core's own EClassImpl is now itself generated code (see NOTES.md's point 6/7
|
|
411
|
-
// write-ups), using the same automatic, get-bodied caching (EObjectImpl.getModelGeneration(),
|
|
412
|
-
// point 1) as self-hosted output always did - there is no recomputeAllLists() method to call
|
|
413
|
-
// anymore, in either mode, and none is needed.
|
|
414
394
|
|
|
415
|
-
// Pass
|
|
416
|
-
//
|
|
417
|
-
// (this.setName(), etc.), which need the "name"/etc. feature's own
|
|
418
|
-
// metaclass object to already exist (it's built above, in the passes
|
|
419
|
-
// this follows). Calling them earlier - before those metaclasses
|
|
420
|
-
// exist - is a genuine bootstrap-ordering bug, confirmed directly by
|
|
421
|
-
// reproducing it: this line order isn't a style choice, the metaclass
|
|
422
|
-
// shells and feature wiring above are a real, load-bearing
|
|
423
|
-
// precondition for the reflective setter calls below to work at all.
|
|
395
|
+
// Pass 5: the package's own name, nsURI, nsPrefix and annotations, last: the setters used here
|
|
396
|
+
// need the features built above.
|
|
424
397
|
this.setName({{ jsString(package.getName()) }});
|
|
425
398
|
this.setNsURI({{ jsString(package.getNsURI()) }});
|
|
426
399
|
this.setNsPrefix({{ jsString(package.getNsPrefix()) }});
|
|
@@ -476,4 +449,9 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
476
449
|
{% endif %}
|
|
477
450
|
{% endfor %}
|
|
478
451
|
}
|
|
452
|
+
|
|
453
|
+
// Lets get{{ pkgClass }}Ref() construct the package on first use.
|
|
454
|
+
register{{ pkgClass }}Initializer(() => {
|
|
455
|
+
void {{ pkgClass }}Impl.eINSTANCE;
|
|
456
|
+
});
|
|
479
457
|
{% endfile %}
|
|
@@ -2,43 +2,41 @@
|
|
|
2
2
|
import type { {{ packageClassName(package) }} } from '../{{ packageClassName(package) }}.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* A
|
|
6
|
-
*
|
|
7
|
-
* it
|
|
8
|
-
*
|
|
9
|
-
* Every generated class needs a reference to the package singleton (for
|
|
10
|
-
* eClass(), for feature-ID lookups in field initializers/lazy getters,
|
|
11
|
-
* etc.) - and {{ packageClassName(package) }}Impl itself constructs
|
|
12
|
-
* (nearly) every one of those classes to build the runtime metamodel. For
|
|
13
|
-
* an ordinary generated package that mutual reference is harmless: the
|
|
14
|
-
* package singleton already exists in full by the time anything else is
|
|
15
|
-
* ever constructed. But self-hosting a metamodel that describes itself
|
|
16
|
-
* (as with real Ecore.ecore) makes this a genuine, confirmed hazard: a
|
|
17
|
-
* real class along the package's own superclass chain, or a class that is
|
|
18
|
-
* ALSO the superclass of something the package constructs directly (the
|
|
19
|
-
* "EFactory unification" / "EPackage unification" cases), can end up
|
|
20
|
-
* needing this reference while {{ packageClassName(package) }}Impl is
|
|
21
|
-
* still in the middle of constructing itself - a genuine circular
|
|
22
|
-
* dependency, not merely an ordering preference, confirmed directly
|
|
23
|
-
* against a real failing bundle ("Class extends value undefined is not a
|
|
24
|
-
* constructor or null") while building this generator, not assumed.
|
|
25
|
-
*
|
|
26
|
-
* This module has only a type-only import (erased at compile time, zero
|
|
27
|
-
* runtime footprint) - so importing FROM this module can never itself
|
|
28
|
-
* participate in a circular dependency, unlike importing
|
|
29
|
-
* {{ packageClassName(package) }}Impl (or anything re-exporting it)
|
|
30
|
-
* directly.
|
|
5
|
+
* A reference to the {{ packageClassName(package) }} singleton that every generated class of the package
|
|
6
|
+
* reads instead of importing {{ packageClassName(package) }}Impl, which would be a circular import: the
|
|
7
|
+
* package constructs those classes while it constructs itself. This module only has a type
|
|
8
|
+
* import, so it can be imported from anywhere.
|
|
31
9
|
*/
|
|
32
10
|
let ref: {{ packageClassName(package) }} | undefined;
|
|
33
11
|
|
|
12
|
+
/** Constructs the package on first use; registered when {{ packageClassName(package) }}Impl.ts is evaluated. */
|
|
13
|
+
let initializer: (() => void) | undefined;
|
|
14
|
+
let initializing = false;
|
|
15
|
+
|
|
34
16
|
export function set{{ packageClassName(package) }}Ref(pkg: {{ packageClassName(package) }}): void {
|
|
35
17
|
ref = pkg;
|
|
36
18
|
}
|
|
37
19
|
|
|
20
|
+
export function register{{ packageClassName(package) }}Initializer(init: () => void): void {
|
|
21
|
+
initializer = init;
|
|
22
|
+
}
|
|
23
|
+
|
|
38
24
|
export function get{{ packageClassName(package) }}Ref(): {{ packageClassName(package) }} {
|
|
25
|
+
// `initializing` turns a reentrant call during construction into the error below.
|
|
26
|
+
if (!ref && initializer && !initializing) {
|
|
27
|
+
initializing = true;
|
|
28
|
+
try {
|
|
29
|
+
initializer();
|
|
30
|
+
} finally {
|
|
31
|
+
initializing = false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
39
34
|
if (!ref) {
|
|
40
35
|
throw new Error(
|
|
41
|
-
'get{{ packageClassName(package) }}Ref() called before the {{ packageClassName(package) }} singleton finished constructing itself
|
|
36
|
+
'get{{ packageClassName(package) }}Ref() called before the {{ packageClassName(package) }} singleton finished constructing itself, and no ' +
|
|
37
|
+
'initializer was registered to construct it on demand - this means {{ packageClassName(package) }}Impl.ts\'s own module ' +
|
|
38
|
+
'was never evaluated at all (an unusual bundler/tree-shaking setup). Call {{ packageClassName(package) }}Impl.eINSTANCE ' +
|
|
39
|
+
'or .init() yourself first, or check that this package is marked side-effectful.'
|
|
42
40
|
);
|
|
43
41
|
}
|
|
44
42
|
return ref;
|
|
@@ -1,22 +1,8 @@
|
|
|
1
1
|
{% file "util/" + typeGuardsClassName(package) + ".ts" %}
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* function isReference(feature: EStructuralFeature): feature is EReference {
|
|
7
|
-
* return "isContainment" in feature;
|
|
8
|
-
* }
|
|
9
|
-
*
|
|
10
|
-
* Structural (duck-typing) checks like that one work when a single
|
|
11
|
-
* classifier has some genuinely distinguishing own member - but nothing
|
|
12
|
-
* guarantees an arbitrary generated classifier has one (a subclass might
|
|
13
|
-
* add no own features at all, inheriting everything). These guards check
|
|
14
|
-
* the object's REAL runtime EClass against the metamodel's own
|
|
15
|
-
* eAllSuperTypes reflectively instead (via isSuperTypeOf()), which is
|
|
16
|
-
* correct for every classifier unconditionally, including abstract ones
|
|
17
|
-
* (correctly true for any instance of a concrete subclass) and dynamic
|
|
18
|
-
* instances (no dependency on the object's actual JS class/prototype at
|
|
19
|
-
* all, unlike `instanceof`).
|
|
3
|
+
* Type guards for the classifiers of the package. They check an object's class with
|
|
4
|
+
* `isSuperTypeOf()`, so they also work for abstract classes, interfaces and dynamic objects,
|
|
5
|
+
* unlike `instanceof`.
|
|
20
6
|
*/
|
|
21
7
|
function hasEClass(obj: unknown): obj is {{ useImport("EObject", "types", {foundational: true}) }} {
|
|
22
8
|
return obj !== null && typeof obj === 'object' && typeof (obj as { eClass?: unknown }).eClass === 'function';
|