@typemf/generator 0.7.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 +21 -46
- package/templates/typescript/efactory.njk +4 -4
- package/templates/typescript/epackage.njk +24 -55
- package/templates/typescript/epackageref.njk +6 -38
- 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!;
|
|
@@ -447,18 +430,9 @@ export class {{ eClass.getName() }}Impl extends {{ (superClass.getName() + "Impl
|
|
|
447
430
|
}
|
|
448
431
|
}
|
|
449
432
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
exists to back the ordinary getter's lazy construction. Constructs
|
|
454
|
-
the cache with no feature reference when called this way - a real,
|
|
455
|
-
narrow, deliberate limitation: containment/opposite maintenance on
|
|
456
|
-
a list built this way is unavailable, since there's no feature to
|
|
457
|
-
derive it from. Acceptable specifically because only the metamodel's
|
|
458
|
-
own self-describing bootstrap instances (EClassImpl, EAttributeImpl,
|
|
459
|
-
etc., as constructed by {Pkg}PackageImpl) ever take this path -
|
|
460
|
-
ordinary domain instances always go through the normal, fully-
|
|
461
|
-
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. */
|
|
462
436
|
eBootstrapList(featureId: number): BasicEList<unknown> {
|
|
463
437
|
switch (featureId) {
|
|
464
438
|
{% for feature in ownFeatures %}
|
|
@@ -476,6 +450,7 @@ export class {{ eClass.getName() }}Impl extends {{ (superClass.getName() + "Impl
|
|
|
476
450
|
{% endif %}
|
|
477
451
|
}
|
|
478
452
|
}
|
|
453
|
+
{% endif %}
|
|
479
454
|
|
|
480
455
|
eIsSet(feature: EStructuralFeature): boolean {
|
|
481
456
|
this.requireOwnFeature(feature);
|
|
@@ -26,10 +26,10 @@ export interface {{ factoryClassName(package) }} extends {{ useImport("EFactory"
|
|
|
26
26
|
double-import the old, pre-refactor version of this template had to
|
|
27
27
|
guard against explicitly. #}
|
|
28
28
|
export class {{ factoryClassName(package) }}Impl extends {{ useImport("EFactoryImpl", "impl", {foundational: true}) }} implements {{ useImport(factoryClassName(package), "root", {foundational: false}) }} {
|
|
29
|
-
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
|
|
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
33
|
static get eINSTANCE(): {{ factoryClassName(package) }} {
|
|
34
34
|
return {{ useImport("get" + pkgClass + "Ref", "impl", {file: pkgClass + "Ref", foundational: false}) }}().getEFactoryInstance() as {{ factoryClassName(package) }};
|
|
35
35
|
}
|
|
@@ -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 %}
|
|
@@ -107,8 +101,9 @@ export interface {{ pkgClass }} extends {{ useImport("EPackage", "types", {found
|
|
|
107
101
|
{% set _ = useImport("register" + pkgClass + "Initializer", "impl", {file: pkgClass + "Ref", foundational: false}) %}
|
|
108
102
|
|
|
109
103
|
{#-
|
|
110
|
-
Emits code reconstructing `element`'s
|
|
111
|
-
|
|
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
|
|
112
107
|
package/classifier/feature/enum-literal only - EOperations have no
|
|
113
108
|
runtime representation in the converted metamodel yet, so there's
|
|
114
109
|
nowhere for an operation-level annotation to attach (see NOTES.md).
|
|
@@ -156,7 +151,7 @@ export interface {{ pkgClass }} extends {{ useImport("EPackage", "types", {found
|
|
|
156
151
|
everywhere else in this file (the metaclass for EStringToStringMapEntry doesn't exist yet at the
|
|
157
152
|
point this code runs during self-construction); the ordinary branch has no such ordering problem and
|
|
158
153
|
uses the real, public API (setDetailValue) directly. #}
|
|
159
|
-
{% for annotation in element
|
|
154
|
+
{% for annotation in runtimeAnnotations(element) %}
|
|
160
155
|
{
|
|
161
156
|
const annotation = new EAnnotationImpl();
|
|
162
157
|
{% if annotation.getSource() %}
|
|
@@ -208,14 +203,9 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
208
203
|
private static _instance: {{ pkgClass }}Impl | undefined;
|
|
209
204
|
|
|
210
205
|
/**
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* overriding createX() to return your own derived classes). Whichever
|
|
215
|
-
* caller gets here FIRST wins; a later call with a *different* factory
|
|
216
|
-
* is logged and otherwise ignored, rather than silently replacing an
|
|
217
|
-
* already-in-use singleton or throwing on a caller who didn't cause the
|
|
218
|
-
* 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.
|
|
219
209
|
*/
|
|
220
210
|
static init(factory?: {{ factoryClassName(package) }}): {{ pkgClass }}Impl {
|
|
221
211
|
if (!{{ pkgClass }}Impl._instance) {
|
|
@@ -236,10 +226,7 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
236
226
|
|
|
237
227
|
private constructor() {
|
|
238
228
|
super();
|
|
239
|
-
// Set
|
|
240
|
-
// which may need this reference - see {{ pkgClass }}Ref.ts's own doc
|
|
241
|
-
// comment for why this is a late-bound reference rather than a
|
|
242
|
-
// direct import of {{ pkgClass }}Impl.
|
|
229
|
+
// Set first: constructing the classifiers below already needs the package reference.
|
|
243
230
|
set{{ pkgClass }}Ref(this);
|
|
244
231
|
|
|
245
232
|
// Pass 1: create every classifier (empty shell - no features/supertypes
|
|
@@ -267,10 +254,7 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
267
254
|
{{ bootstrapSet("literal", "setLiteral", "Ids.EEnumLiteral.literal", jsString(literal.getLiteral())) }}
|
|
268
255
|
{{ emitAnnotations("literal", literal) }}
|
|
269
256
|
{% if options['generate-ecore'] %}
|
|
270
|
-
//
|
|
271
|
-
// hand-written convenience method (it's not a reflective feature),
|
|
272
|
-
// so the list-add and the eEnum backpointer (a real, modeled,
|
|
273
|
-
// non-changeable opposite - see NOTES.md) are wired manually.
|
|
257
|
+
// eEnum is not changeable, so both ends of the opposite are set directly.
|
|
274
258
|
this._{{ classifier.getName() }}.eBootstrapList(Ids.EEnum.eLiterals).add(literal);
|
|
275
259
|
literal.eBasicSetValue(Ids.EEnumLiteral.eEnum, this._{{ classifier.getName() }});
|
|
276
260
|
{% else %}
|
|
@@ -326,6 +310,9 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
326
310
|
{{ bootstrapSet(fieldExpr, "setChangeable", "Ids.EStructuralFeature.changeable", feature.isChangeable()) }}
|
|
327
311
|
{{ bootstrapSet(fieldExpr, "setOrdered", "Ids.ETypedElement.ordered", feature.isOrdered()) }}
|
|
328
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 %}
|
|
329
316
|
{{ fieldExpr }}.setFeatureID(Ids.{{ classifier.getName() }}.{{ feature.getName() }});
|
|
330
317
|
{#- containerClass: the concrete implementation class the feature is declared on - the closest
|
|
331
318
|
TypeScript has to Java's own containerClass semantics (Class<?>, e.g. EClassifier.class in the
|
|
@@ -346,11 +333,8 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
346
333
|
{% endfor %}
|
|
347
334
|
{% endfor %}
|
|
348
335
|
|
|
349
|
-
// Pass 2.5: operations and their parameters
|
|
350
|
-
//
|
|
351
|
-
// this is what actually populates it, the same way pass 2 populates eStructuralFeatures. Kept as
|
|
352
|
-
// its own pass, after every classifier and feature exists (an operation's return type or a
|
|
353
|
-
// 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.
|
|
354
338
|
{% for classifier in eClassesOf(package) %}
|
|
355
339
|
{% for op in classifier.getEOperations() %}
|
|
356
340
|
{% set opIndex = loop.index0 %}
|
|
@@ -407,21 +391,9 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
407
391
|
{% endfor %}
|
|
408
392
|
{% endfor %}
|
|
409
393
|
|
|
410
|
-
// Pass 5 (recompute EAll* caches, ordinary mode only) removed - stale post-swap.
|
|
411
|
-
// @typemf/core's own EClassImpl is now itself generated code (see NOTES.md's point 6/7
|
|
412
|
-
// write-ups), using the same automatic, get-bodied caching (EObjectImpl.getModelGeneration(),
|
|
413
|
-
// point 1) as self-hosted output always did - there is no recomputeAllLists() method to call
|
|
414
|
-
// anymore, in either mode, and none is needed.
|
|
415
394
|
|
|
416
|
-
// Pass
|
|
417
|
-
//
|
|
418
|
-
// (this.setName(), etc.), which need the "name"/etc. feature's own
|
|
419
|
-
// metaclass object to already exist (it's built above, in the passes
|
|
420
|
-
// this follows). Calling them earlier - before those metaclasses
|
|
421
|
-
// exist - is a genuine bootstrap-ordering bug, confirmed directly by
|
|
422
|
-
// reproducing it: this line order isn't a style choice, the metaclass
|
|
423
|
-
// shells and feature wiring above are a real, load-bearing
|
|
424
|
-
// 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.
|
|
425
397
|
this.setName({{ jsString(package.getName()) }});
|
|
426
398
|
this.setNsURI({{ jsString(package.getNsURI()) }});
|
|
427
399
|
this.setNsPrefix({{ jsString(package.getNsPrefix()) }});
|
|
@@ -478,10 +450,7 @@ export class {{ pkgClass }}Impl extends EPackageImpl implements {{ pkgClass }} {
|
|
|
478
450
|
{% endfor %}
|
|
479
451
|
}
|
|
480
452
|
|
|
481
|
-
//
|
|
482
|
-
// throwing when nothing has touched {{ pkgClass }}Impl.eINSTANCE/.init() yet. A side effect of
|
|
483
|
-
// this module being evaluated at all - see register{{ pkgClass }}Initializer's own doc comment
|
|
484
|
-
// in {{ pkgClass }}Ref.ts.
|
|
453
|
+
// Lets get{{ pkgClass }}Ref() construct the package on first use.
|
|
485
454
|
register{{ pkgClass }}Initializer(() => {
|
|
486
455
|
void {{ pkgClass }}Impl.eINSTANCE;
|
|
487
456
|
});
|
|
@@ -2,43 +2,14 @@
|
|
|
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
|
|
|
34
|
-
/**
|
|
35
|
-
* Set once, as a module-scope side effect of {{ packageClassName(package) }}Impl.ts's own module
|
|
36
|
-
* being evaluated (see the call at the bottom of that file) - never a direct import of
|
|
37
|
-
* {{ packageClassName(package) }}Impl here, for the exact same circularity reason the class doc
|
|
38
|
-
* comment above explains. Lets get{{ packageClassName(package) }}Ref() (CORE-01) construct the
|
|
39
|
-
* singleton itself, on first use, instead of only ever throwing when nothing has touched
|
|
40
|
-
* {{ packageClassName(package) }}Impl.eINSTANCE/.init() yet.
|
|
41
|
-
*/
|
|
12
|
+
/** Constructs the package on first use; registered when {{ packageClassName(package) }}Impl.ts is evaluated. */
|
|
42
13
|
let initializer: (() => void) | undefined;
|
|
43
14
|
let initializing = false;
|
|
44
15
|
|
|
@@ -51,10 +22,7 @@ export function register{{ packageClassName(package) }}Initializer(init: () => v
|
|
|
51
22
|
}
|
|
52
23
|
|
|
53
24
|
export function get{{ packageClassName(package) }}Ref(): {{ packageClassName(package) }} {
|
|
54
|
-
//
|
|
55
|
-
// set{{ packageClassName(package) }}Ref(this) - which should never genuinely happen; the
|
|
56
|
-
// constructor calls that first, before anything else that could call back in here - into the
|
|
57
|
-
// ordinary "not ready yet" error below instead of unbounded recursion.
|
|
25
|
+
// `initializing` turns a reentrant call during construction into the error below.
|
|
58
26
|
if (!ref && initializer && !initializing) {
|
|
59
27
|
initializing = true;
|
|
60
28
|
try {
|
|
@@ -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';
|