@typemf/generator 0.8.1 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/cli.js +134 -14
- package/dist/index.d.ts +18 -6
- package/dist/index.js +137 -15
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/templates/typescript/epackage.njk +2 -2
package/README.md
CHANGED
|
@@ -28,6 +28,24 @@ The config file defaults to `./typemf-generator.config.json`:
|
|
|
28
28
|
|
|
29
29
|
Paths are resolved relative to the config file. `typescript` is currently the only template set.
|
|
30
30
|
|
|
31
|
+
## Other packages
|
|
32
|
+
|
|
33
|
+
A `.ecore` file can use classifiers of another `.ecore` file (`href="other.ecore#//Shape"`). The
|
|
34
|
+
generator loads that file too, but doesn't generate it; generate it with its own config. The
|
|
35
|
+
option `package-imports` says where its generated code is, by nsURI:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
"options": {
|
|
39
|
+
"package-imports": {
|
|
40
|
+
"https://example.com/shapes": "../shapes"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
A value starting with `./` or `../` is the folder of the other package's generated code, relative
|
|
46
|
+
to `outputDir`. Any other value is a module name that exports its types and its `PackageImpl`.
|
|
47
|
+
Classifiers of Ecore need no entry. Supertypes from another package are not supported yet.
|
|
48
|
+
|
|
31
49
|
## Generated code
|
|
32
50
|
|
|
33
51
|
For a package `library` with the classes `Library`, `Writer` and `Book`:
|
package/dist/cli.js
CHANGED
|
@@ -42,7 +42,13 @@ function validateConfig(value, configPath) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// src/ecore-loader.ts
|
|
45
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
EcorePackageImpl as EcorePackageImpl2,
|
|
47
|
+
getProxyURI as getProxyURI2,
|
|
48
|
+
isEReference,
|
|
49
|
+
ResourceSetImpl,
|
|
50
|
+
URI
|
|
51
|
+
} from "@typemf/core";
|
|
46
52
|
import { NodeFileUriConverter } from "@typemf/node";
|
|
47
53
|
import { registerXmiFormat } from "@typemf/xmi";
|
|
48
54
|
|
|
@@ -66,7 +72,7 @@ import {
|
|
|
66
72
|
isEClassifier,
|
|
67
73
|
resolveAgainstPackage
|
|
68
74
|
} from "@typemf/core";
|
|
69
|
-
function convertDynamicEcoreToTyped(dynamicPkg) {
|
|
75
|
+
function convertDynamicEcoreToTyped(dynamicPkg, foreignClassifier) {
|
|
70
76
|
const realPkg = new EPackageImpl();
|
|
71
77
|
realPkg.setName(byName(dynamicPkg, "name") ?? "");
|
|
72
78
|
realPkg.setNsURI(byName(dynamicPkg, "nsURI") ?? "");
|
|
@@ -102,6 +108,8 @@ function convertDynamicEcoreToTyped(dynamicPkg) {
|
|
|
102
108
|
}
|
|
103
109
|
function classifierOf(dynClassifier) {
|
|
104
110
|
if (classifierMap.has(dynClassifier)) return classifierMap.get(dynClassifier);
|
|
111
|
+
const foreign = foreignClassifier?.(dynClassifier);
|
|
112
|
+
if (foreign) return foreign;
|
|
105
113
|
const uri = dynClassifier.eIsProxy() ? getProxyURI(dynClassifier) : void 0;
|
|
106
114
|
const ecore = EcorePackageImpl.eINSTANCE;
|
|
107
115
|
if (uri && uri.trimFragment().toString() === ecore.getNsURI()) {
|
|
@@ -111,7 +119,7 @@ function convertDynamicEcoreToTyped(dynamicPkg) {
|
|
|
111
119
|
if (isEClassifier(resolved)) return resolved;
|
|
112
120
|
}
|
|
113
121
|
throw new Error(
|
|
114
|
-
`Cannot resolve the classifier reference '${uri?.toString() ?? dynClassifier.fullId()}': only classifiers of this package and of
|
|
122
|
+
`Cannot resolve the classifier reference '${uri?.toString() ?? dynClassifier.fullId()}': only classifiers of this package, of Ecore and of other .ecore files are supported.`
|
|
115
123
|
);
|
|
116
124
|
}
|
|
117
125
|
function resolveType(dynFeature) {
|
|
@@ -692,12 +700,57 @@ async function loadEcorePackage(ecoreFilePath) {
|
|
|
692
700
|
if (!resource) {
|
|
693
701
|
throw new Error(`Could not load '${ecoreFilePath}'.`);
|
|
694
702
|
}
|
|
695
|
-
|
|
696
|
-
|
|
703
|
+
return convertResource(resource, resourceSet, /* @__PURE__ */ new Map(), []);
|
|
704
|
+
}
|
|
705
|
+
async function convertResource(resource, resourceSet, converted, inProgress) {
|
|
706
|
+
const done = converted.get(resource);
|
|
707
|
+
if (done) return done;
|
|
708
|
+
const location = resource.getURI().toString();
|
|
709
|
+
if (inProgress.includes(resource)) {
|
|
710
|
+
const cycle = [...inProgress, resource].map((r) => r.getURI().toString()).join(" -> ");
|
|
711
|
+
throw new Error(`Cyclic references between .ecore files are not supported: ${cycle}`);
|
|
712
|
+
}
|
|
713
|
+
const dynamicPkg = resource.getContents().toArray()[0];
|
|
697
714
|
if (!dynamicPkg) {
|
|
698
|
-
throw new Error(`'${
|
|
715
|
+
throw new Error(`'${location}' has no root element - expected a single EPackage.`);
|
|
716
|
+
}
|
|
717
|
+
const foreign = /* @__PURE__ */ new Map();
|
|
718
|
+
for (const proxy of foreignProxies(dynamicPkg)) {
|
|
719
|
+
const target = await resourceSet.resolve(proxy).catch((err) => {
|
|
720
|
+
throw new Error(
|
|
721
|
+
`Cannot resolve the classifier reference '${getProxyURI2(proxy)?.toString()}': ${err.message}`,
|
|
722
|
+
{ cause: err }
|
|
723
|
+
);
|
|
724
|
+
});
|
|
725
|
+
const targetResource = target.eResource();
|
|
726
|
+
const targetPkg = targetResource ? await convertResource(targetResource, resourceSet, converted, [...inProgress, resource]) : void 0;
|
|
727
|
+
const name = target.eClass().getEStructuralFeature("name");
|
|
728
|
+
const classifier = name && targetPkg?.getEClassifier(target.eGet(name));
|
|
729
|
+
if (!classifier) {
|
|
730
|
+
throw new Error(
|
|
731
|
+
`Cannot resolve the classifier reference '${getProxyURI2(proxy)?.toString()}': only classifiers of a referenced package's root are supported.`
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
foreign.set(proxy, classifier);
|
|
735
|
+
}
|
|
736
|
+
const pkg = convertDynamicEcoreToTyped(dynamicPkg, (proxy) => foreign.get(proxy));
|
|
737
|
+
converted.set(resource, pkg);
|
|
738
|
+
return pkg;
|
|
739
|
+
}
|
|
740
|
+
function foreignProxies(root) {
|
|
741
|
+
const ecoreNsURI = EcorePackageImpl2.eINSTANCE.getNsURI();
|
|
742
|
+
const proxies = /* @__PURE__ */ new Set();
|
|
743
|
+
for (const obj of [root, ...root.eAllContents()]) {
|
|
744
|
+
for (const feature of obj.eClass().getEAllStructuralFeatures()) {
|
|
745
|
+
if (!isEReference(feature) || feature.isContainment()) continue;
|
|
746
|
+
const value = obj.eGet(feature);
|
|
747
|
+
const values = feature.isMany() ? [...value] : value ? [value] : [];
|
|
748
|
+
for (const v of values) {
|
|
749
|
+
if (v.eIsProxy() && getProxyURI2(v)?.trimFragment().toString() !== ecoreNsURI) proxies.add(v);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
699
752
|
}
|
|
700
|
-
return
|
|
753
|
+
return proxies;
|
|
701
754
|
}
|
|
702
755
|
|
|
703
756
|
// src/run-generation.ts
|
|
@@ -821,16 +874,26 @@ var TypeImportMapping = class _TypeImportMapping {
|
|
|
821
874
|
var generationContext = {
|
|
822
875
|
pkg: void 0,
|
|
823
876
|
typeImports: new TypeImportMapping(),
|
|
824
|
-
generateEcore: false
|
|
877
|
+
generateEcore: false,
|
|
878
|
+
packageImports: /* @__PURE__ */ new Map()
|
|
825
879
|
};
|
|
826
880
|
var GENERATE_ECORE_OPTION = "generate-ecore";
|
|
827
881
|
var TYPE_IMPORTS_OPTION = "type-imports";
|
|
882
|
+
var PACKAGE_IMPORTS_OPTION = "package-imports";
|
|
828
883
|
function beginGeneration(pkg, options) {
|
|
829
884
|
generationContext.pkg = pkg;
|
|
830
885
|
const entries = options[TYPE_IMPORTS_OPTION];
|
|
831
886
|
const valid = typeof entries === "object" && entries !== null && !Array.isArray(entries);
|
|
832
887
|
generationContext.typeImports = new TypeImportMapping(valid ? entries : {});
|
|
833
888
|
generationContext.generateEcore = options[GENERATE_ECORE_OPTION] === true;
|
|
889
|
+
generationContext.packageImports = new Map(
|
|
890
|
+
Object.entries(objectOption(options[PACKAGE_IMPORTS_OPTION])).filter(
|
|
891
|
+
(entry) => typeof entry[1] === "string" && entry[1] !== ""
|
|
892
|
+
)
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
function objectOption(value) {
|
|
896
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
834
897
|
}
|
|
835
898
|
function isExternal(classifier) {
|
|
836
899
|
const owner = classifier.getEPackage();
|
|
@@ -929,8 +992,10 @@ function locationOfPath(path) {
|
|
|
929
992
|
}
|
|
930
993
|
|
|
931
994
|
// src/typescript-filters.ts
|
|
995
|
+
import { posix as posix2 } from "path";
|
|
932
996
|
import {
|
|
933
997
|
detailValue as detailValue2,
|
|
998
|
+
EcorePackageImpl as EcorePackageImpl3,
|
|
934
999
|
isEDataType as isDataTypeObject
|
|
935
1000
|
} from "@typemf/core";
|
|
936
1001
|
var TYPEMF_GENERATOR_ANNOTATION_SOURCE = "https://typemf.dev/generator";
|
|
@@ -1065,8 +1130,60 @@ function tsBaseType(classifier) {
|
|
|
1065
1130
|
return "EObject";
|
|
1066
1131
|
}
|
|
1067
1132
|
if (isEDataType(classifier)) return tsPrimitiveType(classifier);
|
|
1133
|
+
if (isExternal(classifier)) {
|
|
1134
|
+
const name = classifier.getName();
|
|
1135
|
+
if (isEcoreClassifier(classifier)) importCollector.add({ name, location: "types", foundational: true });
|
|
1136
|
+
else
|
|
1137
|
+
importCollector.add({
|
|
1138
|
+
name,
|
|
1139
|
+
location: "types",
|
|
1140
|
+
foundational: false,
|
|
1141
|
+
from: otherPackageImport(classifier.getEPackage(), "types", name)
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1068
1144
|
return classifier.getName();
|
|
1069
1145
|
}
|
|
1146
|
+
function isEcoreClassifier(classifier) {
|
|
1147
|
+
return classifier.getEPackage()?.getNsURI() === EcorePackageImpl3.eINSTANCE.getNsURI();
|
|
1148
|
+
}
|
|
1149
|
+
function otherPackageImport(pkg, folder, file) {
|
|
1150
|
+
const nsURI = pkg.getNsURI() ?? "";
|
|
1151
|
+
const location = generationContext.packageImports.get(nsURI);
|
|
1152
|
+
if (location === void 0) {
|
|
1153
|
+
throw new Error(
|
|
1154
|
+
`The package '${nsURI}' is referenced, but the "${PACKAGE_IMPORTS_OPTION}" option has no entry for it.`
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
if (!location.startsWith("./") && !location.startsWith("../")) return location;
|
|
1158
|
+
const path = posix2.join(location, folder, `${file}.js`);
|
|
1159
|
+
return path.startsWith(".") ? path : `./${path}`;
|
|
1160
|
+
}
|
|
1161
|
+
function findOtherPackageSuperTypes(pkg) {
|
|
1162
|
+
const messages = [];
|
|
1163
|
+
for (const classifier of pkg.getEClassifiers()) {
|
|
1164
|
+
if (!isEClass2(classifier)) continue;
|
|
1165
|
+
for (const superType2 of classifier.getESuperTypes()) {
|
|
1166
|
+
if (superType2.getEPackage() !== pkg)
|
|
1167
|
+
messages.push(
|
|
1168
|
+
`${classifier.getName()}: the supertype ${superType2.getName()} belongs to another package, which is not supported yet`
|
|
1169
|
+
);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
return messages;
|
|
1173
|
+
}
|
|
1174
|
+
function otherPackageClassifierExpr(classifier) {
|
|
1175
|
+
const pkg = classifier.getEPackage();
|
|
1176
|
+
const implName = isEcoreClassifier(classifier) ? "EcorePackageImpl" : `${packageClassName(pkg)}Impl`;
|
|
1177
|
+
if (isEcoreClassifier(classifier)) importCollector.add({ name: implName, location: "impl", foundational: true });
|
|
1178
|
+
else
|
|
1179
|
+
importCollector.add({
|
|
1180
|
+
name: implName,
|
|
1181
|
+
location: "impl",
|
|
1182
|
+
foundational: false,
|
|
1183
|
+
from: otherPackageImport(pkg, "impl", implName)
|
|
1184
|
+
});
|
|
1185
|
+
return `${implName}.eINSTANCE.get${classifier.getName()}()`;
|
|
1186
|
+
}
|
|
1070
1187
|
function resolveDataTypeTs(dataType) {
|
|
1071
1188
|
const resolved = resolveDataTypeText(dataType);
|
|
1072
1189
|
const importInfo = generationContext.typeImports.lookup(dataType);
|
|
@@ -1161,7 +1278,7 @@ function isEEnum(classifier) {
|
|
|
1161
1278
|
function isEDataType(classifier) {
|
|
1162
1279
|
return !isEClass2(classifier) && !isEEnum(classifier);
|
|
1163
1280
|
}
|
|
1164
|
-
function
|
|
1281
|
+
function isEReference2(feature) {
|
|
1165
1282
|
return "isContainment" in feature;
|
|
1166
1283
|
}
|
|
1167
1284
|
function paramList(operation) {
|
|
@@ -1478,9 +1595,10 @@ function referencedApiTypes(features, ...excludeTypeNames) {
|
|
|
1478
1595
|
const names = /* @__PURE__ */ new Set();
|
|
1479
1596
|
for (const feature of features) {
|
|
1480
1597
|
const type = feature.getEType();
|
|
1481
|
-
if (type && (
|
|
1598
|
+
if (type && (isEReference2(feature) || isEEnum(type)) && !isExternal(type) && !exclude.has(type.getName()))
|
|
1599
|
+
names.add(type.getName());
|
|
1482
1600
|
for (const argument of genericArgumentClassifiers(feature.getEGenericType())) {
|
|
1483
|
-
if ((isEClass2(argument) || isEEnum(argument)) && !exclude.has(argument.getName()))
|
|
1601
|
+
if ((isEClass2(argument) || isEEnum(argument)) && !isExternal(argument) && !exclude.has(argument.getName()))
|
|
1484
1602
|
names.add(argument.getName());
|
|
1485
1603
|
}
|
|
1486
1604
|
}
|
|
@@ -1490,7 +1608,7 @@ function referencedOperationTypes(operations, ...excludeTypeNames) {
|
|
|
1490
1608
|
const exclude = new Set(excludeTypeNames);
|
|
1491
1609
|
const names = /* @__PURE__ */ new Set();
|
|
1492
1610
|
const maybeAdd = (type) => {
|
|
1493
|
-
if (type && (isEClass2(type) || isEEnum(type)) && !exclude.has(type.getName())) {
|
|
1611
|
+
if (type && (isEClass2(type) || isEEnum(type)) && !isExternal(type) && !exclude.has(type.getName())) {
|
|
1494
1612
|
names.add(type.getName());
|
|
1495
1613
|
}
|
|
1496
1614
|
};
|
|
@@ -1553,7 +1671,8 @@ var typescriptTemplateSet = {
|
|
|
1553
1671
|
...findUnnamedElements(pkg),
|
|
1554
1672
|
...findUnresolvedCollisions(pkg).map(
|
|
1555
1673
|
(c) => `${c.classifierName}.${c.memberName}: name collision between ${c.sources.join(" and ")}`
|
|
1556
|
-
)
|
|
1674
|
+
),
|
|
1675
|
+
...findOtherPackageSuperTypes(pkg)
|
|
1557
1676
|
];
|
|
1558
1677
|
},
|
|
1559
1678
|
configureEnvironment(env, { pkg, options }) {
|
|
@@ -1575,7 +1694,7 @@ var typescriptTemplateSet = {
|
|
|
1575
1694
|
env.addGlobal("isEClass", isEClass2);
|
|
1576
1695
|
env.addGlobal("isEEnum", isEEnum);
|
|
1577
1696
|
env.addGlobal("isEDataType", isEDataType);
|
|
1578
|
-
env.addGlobal("isEReference",
|
|
1697
|
+
env.addGlobal("isEReference", isEReference2);
|
|
1579
1698
|
env.addGlobal("paramList", paramList);
|
|
1580
1699
|
env.addGlobal("argList", argList);
|
|
1581
1700
|
env.addGlobal("ucfirst", ucfirst);
|
|
@@ -1607,6 +1726,7 @@ var typescriptTemplateSet = {
|
|
|
1607
1726
|
env.addGlobal("metaclassAccessorCollision", metaclassAccessorCollision);
|
|
1608
1727
|
env.addGlobal("metaclassAccessorCollisionTypeNames", metaclassAccessorCollisionTypeNames);
|
|
1609
1728
|
env.addGlobal("optionalParamList", optionalParamList);
|
|
1729
|
+
env.addGlobal("otherPackageClassifierExpr", otherPackageClassifierExpr);
|
|
1610
1730
|
env.addGlobal("typeGuardsClassName", typeGuardsClassName);
|
|
1611
1731
|
env.addGlobal("useImport", useImport);
|
|
1612
1732
|
},
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EPackage, EObject, EPackageImpl, EClassImpl, EOperation, EStructuralFeature, EClass, EAnnotation, EModelElement, EGenericType,
|
|
1
|
+
import { EPackage, EObject, EClassifier, EPackageImpl, EClassImpl, EOperation, EStructuralFeature, EClass, EAnnotation, EModelElement, EGenericType, EDataType, EEnum, ETypedElement } from '@typemf/core';
|
|
2
2
|
import nunjucks from 'nunjucks';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -62,8 +62,11 @@ declare function loadEcorePackage(ecoreFilePath: string): Promise<EPackage>;
|
|
|
62
62
|
* classifier here: any reference to it becomes `undefined` on the real
|
|
63
63
|
* side, matching the generator's own existing convention for a classifier
|
|
64
64
|
* with no explicit type (which already means "real @typemf/core EObject").
|
|
65
|
+
*
|
|
66
|
+
* `foreignClassifier` maps a proxy into another .ecore file to the typed classifier it stands for
|
|
67
|
+
* (see loadEcorePackage); without it, only this package's and Ecore's classifiers can be referenced.
|
|
65
68
|
*/
|
|
66
|
-
declare function convertDynamicEcoreToTyped(dynamicPkg: EObject): EPackage;
|
|
69
|
+
declare function convertDynamicEcoreToTyped(dynamicPkg: EObject, foreignClassifier?: (proxy: EObject) => EClassifier | undefined): EPackage;
|
|
67
70
|
|
|
68
71
|
/**
|
|
69
72
|
* A hand-authored graph mirroring real Ecore.ecore's own full structure -
|
|
@@ -381,6 +384,13 @@ declare function tsScalarType(type: EClassifier | ETypedElement | undefined): st
|
|
|
381
384
|
* imports the emitted type arguments require.
|
|
382
385
|
*/
|
|
383
386
|
declare function genericArgumentClassifiers(genericType: EGenericType | undefined): EClassifier[];
|
|
387
|
+
/** A message for each class of `pkg` with a supertype from another package, which is not supported. */
|
|
388
|
+
declare function findOtherPackageSuperTypes(pkg: EPackage): string[];
|
|
389
|
+
/**
|
|
390
|
+
* The expression for a classifier of another package in the generated package class: the accessor
|
|
391
|
+
* on that package's PackageImpl singleton, whose import it registers.
|
|
392
|
+
*/
|
|
393
|
+
declare function otherPackageClassifierExpr(classifier: EClassifier): string;
|
|
384
394
|
interface ResolvedDataTypeText {
|
|
385
395
|
/** The TypeScript type text to emit wherever this datatype is used. */
|
|
386
396
|
text: string;
|
|
@@ -655,9 +665,11 @@ interface MemberCollision {
|
|
|
655
665
|
declare function findUnnamedElements(pkg: EPackage): string[];
|
|
656
666
|
declare function findUnresolvedCollisions(pkg: EPackage): MemberCollision[];
|
|
657
667
|
/**
|
|
658
|
-
* Distinct EClass/EEnum type names referenced by `features`
|
|
659
|
-
* EEnum's own attribute/reference types) that need their own
|
|
660
|
-
* statement - EDataType primitives (string/number/boolean/Date) never
|
|
668
|
+
* Distinct EClass/EEnum type names of this package referenced by `features`
|
|
669
|
+
* (an EClass or EEnum's own attribute/reference types) that need their own
|
|
670
|
+
* import statement - EDataType primitives (string/number/boolean/Date) never
|
|
671
|
+
* do, and other packages' types are imported where they are emitted (see
|
|
672
|
+
* tsBaseType).
|
|
661
673
|
* A TS helper rather than a Nunjucks loop with manual dedup, since
|
|
662
674
|
* template-level array mutation is awkward without extra Nunjucks
|
|
663
675
|
* extensions - matches the "templates handle structure, TS computes"
|
|
@@ -735,4 +747,4 @@ declare function singleValuedFeatures(features: Iterable<EStructuralFeature>): E
|
|
|
735
747
|
|
|
736
748
|
declare const typescriptTemplateSet: TemplateSet;
|
|
737
749
|
|
|
738
|
-
export { ECLIPSE_ECORE_ANNOTATION_SOURCE, ECLIPSE_GENMODEL_ANNOTATION_SOURCE, FEATURE_ANNOTATION_SOURCE, type GeneratedFile, type GeneratorConfig, type MemberCollision, type OverloadBranch, type OverloadDispatch, type ResolvedDataTypeText, type RunGenerationResult, TYPEMF_GENERATOR_ANNOTATION_SOURCE, type TemplateSet, argList, assignFreshIds, beanGetterName, buildEcoreMetaSchema, concreteEClassesOf, convertDynamicEcoreToTyped, customBodyNeedsOwnField, detailsEntries, docComment, documentationOf, eClassesOf, factoryClassName, featureGetter, featureSetter, findEClassByName, findFeatureAnnotationProblems, findUnnamedElements, findUnresolvedCollisions, generate, genericArgumentClassifiers, groupOperationsByName, hasStoredManyValuedFeature, isBookkeepingOperation, isClassifierDerived, isEClass, isEDataType, isEEnum, isEReference, isOperationDerived, isOptional, isPrimitiveValueType, isReadOnlyFeature, isStructuralFeatureDerived, jsString, layeredAnnotationDetail, loadConfig, loadEcorePackage, mergedParamList, mergedReturnType, metaclassAccessorCollision, metaclassAccessorCollisionTypeNames, needsImplOnlySetter, operationBody, optionalParamList, overloadDispatch, packageClassName, paramList, primitiveDefaultValue, referencedApiTypes, referencedOperationTypes, registerBodyImports, resolveDataTypeTs, resolveTemplateSet, runGeneration, runtimeAnnotations, singleValuedFeatures, superType, superTypeChain, switchClassName, trivialDerivedFormula, tsFeatureType, tsOperationReturnType, tsOptionalScalarType, tsScalarType, typeGuardsClassName, typescriptTemplateSet, ucfirst };
|
|
750
|
+
export { ECLIPSE_ECORE_ANNOTATION_SOURCE, ECLIPSE_GENMODEL_ANNOTATION_SOURCE, FEATURE_ANNOTATION_SOURCE, type GeneratedFile, type GeneratorConfig, type MemberCollision, type OverloadBranch, type OverloadDispatch, type ResolvedDataTypeText, type RunGenerationResult, TYPEMF_GENERATOR_ANNOTATION_SOURCE, type TemplateSet, argList, assignFreshIds, beanGetterName, buildEcoreMetaSchema, concreteEClassesOf, convertDynamicEcoreToTyped, customBodyNeedsOwnField, detailsEntries, docComment, documentationOf, eClassesOf, factoryClassName, featureGetter, featureSetter, findEClassByName, findFeatureAnnotationProblems, findOtherPackageSuperTypes, findUnnamedElements, findUnresolvedCollisions, generate, genericArgumentClassifiers, groupOperationsByName, hasStoredManyValuedFeature, isBookkeepingOperation, isClassifierDerived, isEClass, isEDataType, isEEnum, isEReference, isOperationDerived, isOptional, isPrimitiveValueType, isReadOnlyFeature, isStructuralFeatureDerived, jsString, layeredAnnotationDetail, loadConfig, loadEcorePackage, mergedParamList, mergedReturnType, metaclassAccessorCollision, metaclassAccessorCollisionTypeNames, needsImplOnlySetter, operationBody, optionalParamList, otherPackageClassifierExpr, overloadDispatch, packageClassName, paramList, primitiveDefaultValue, referencedApiTypes, referencedOperationTypes, registerBodyImports, resolveDataTypeTs, resolveTemplateSet, runGeneration, runtimeAnnotations, singleValuedFeatures, superType, superTypeChain, switchClassName, trivialDerivedFormula, tsFeatureType, tsOperationReturnType, tsOptionalScalarType, tsScalarType, typeGuardsClassName, typescriptTemplateSet, ucfirst };
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,13 @@ function validateConfig(value, configPath) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// src/ecore-loader.ts
|
|
40
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
EcorePackageImpl as EcorePackageImpl2,
|
|
42
|
+
getProxyURI as getProxyURI2,
|
|
43
|
+
isEReference,
|
|
44
|
+
ResourceSetImpl,
|
|
45
|
+
URI
|
|
46
|
+
} from "@typemf/core";
|
|
41
47
|
import { NodeFileUriConverter } from "@typemf/node";
|
|
42
48
|
import { registerXmiFormat } from "@typemf/xmi";
|
|
43
49
|
|
|
@@ -61,7 +67,7 @@ import {
|
|
|
61
67
|
isEClassifier,
|
|
62
68
|
resolveAgainstPackage
|
|
63
69
|
} from "@typemf/core";
|
|
64
|
-
function convertDynamicEcoreToTyped(dynamicPkg) {
|
|
70
|
+
function convertDynamicEcoreToTyped(dynamicPkg, foreignClassifier) {
|
|
65
71
|
const realPkg = new EPackageImpl();
|
|
66
72
|
realPkg.setName(byName(dynamicPkg, "name") ?? "");
|
|
67
73
|
realPkg.setNsURI(byName(dynamicPkg, "nsURI") ?? "");
|
|
@@ -97,6 +103,8 @@ function convertDynamicEcoreToTyped(dynamicPkg) {
|
|
|
97
103
|
}
|
|
98
104
|
function classifierOf(dynClassifier) {
|
|
99
105
|
if (classifierMap.has(dynClassifier)) return classifierMap.get(dynClassifier);
|
|
106
|
+
const foreign = foreignClassifier?.(dynClassifier);
|
|
107
|
+
if (foreign) return foreign;
|
|
100
108
|
const uri = dynClassifier.eIsProxy() ? getProxyURI(dynClassifier) : void 0;
|
|
101
109
|
const ecore = EcorePackageImpl.eINSTANCE;
|
|
102
110
|
if (uri && uri.trimFragment().toString() === ecore.getNsURI()) {
|
|
@@ -106,7 +114,7 @@ function convertDynamicEcoreToTyped(dynamicPkg) {
|
|
|
106
114
|
if (isEClassifier(resolved)) return resolved;
|
|
107
115
|
}
|
|
108
116
|
throw new Error(
|
|
109
|
-
`Cannot resolve the classifier reference '${uri?.toString() ?? dynClassifier.fullId()}': only classifiers of this package and of
|
|
117
|
+
`Cannot resolve the classifier reference '${uri?.toString() ?? dynClassifier.fullId()}': only classifiers of this package, of Ecore and of other .ecore files are supported.`
|
|
110
118
|
);
|
|
111
119
|
}
|
|
112
120
|
function resolveType(dynFeature) {
|
|
@@ -687,12 +695,57 @@ async function loadEcorePackage(ecoreFilePath) {
|
|
|
687
695
|
if (!resource) {
|
|
688
696
|
throw new Error(`Could not load '${ecoreFilePath}'.`);
|
|
689
697
|
}
|
|
690
|
-
|
|
691
|
-
|
|
698
|
+
return convertResource(resource, resourceSet, /* @__PURE__ */ new Map(), []);
|
|
699
|
+
}
|
|
700
|
+
async function convertResource(resource, resourceSet, converted, inProgress) {
|
|
701
|
+
const done = converted.get(resource);
|
|
702
|
+
if (done) return done;
|
|
703
|
+
const location = resource.getURI().toString();
|
|
704
|
+
if (inProgress.includes(resource)) {
|
|
705
|
+
const cycle = [...inProgress, resource].map((r) => r.getURI().toString()).join(" -> ");
|
|
706
|
+
throw new Error(`Cyclic references between .ecore files are not supported: ${cycle}`);
|
|
707
|
+
}
|
|
708
|
+
const dynamicPkg = resource.getContents().toArray()[0];
|
|
692
709
|
if (!dynamicPkg) {
|
|
693
|
-
throw new Error(`'${
|
|
710
|
+
throw new Error(`'${location}' has no root element - expected a single EPackage.`);
|
|
711
|
+
}
|
|
712
|
+
const foreign = /* @__PURE__ */ new Map();
|
|
713
|
+
for (const proxy of foreignProxies(dynamicPkg)) {
|
|
714
|
+
const target = await resourceSet.resolve(proxy).catch((err) => {
|
|
715
|
+
throw new Error(
|
|
716
|
+
`Cannot resolve the classifier reference '${getProxyURI2(proxy)?.toString()}': ${err.message}`,
|
|
717
|
+
{ cause: err }
|
|
718
|
+
);
|
|
719
|
+
});
|
|
720
|
+
const targetResource = target.eResource();
|
|
721
|
+
const targetPkg = targetResource ? await convertResource(targetResource, resourceSet, converted, [...inProgress, resource]) : void 0;
|
|
722
|
+
const name = target.eClass().getEStructuralFeature("name");
|
|
723
|
+
const classifier = name && targetPkg?.getEClassifier(target.eGet(name));
|
|
724
|
+
if (!classifier) {
|
|
725
|
+
throw new Error(
|
|
726
|
+
`Cannot resolve the classifier reference '${getProxyURI2(proxy)?.toString()}': only classifiers of a referenced package's root are supported.`
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
foreign.set(proxy, classifier);
|
|
730
|
+
}
|
|
731
|
+
const pkg = convertDynamicEcoreToTyped(dynamicPkg, (proxy) => foreign.get(proxy));
|
|
732
|
+
converted.set(resource, pkg);
|
|
733
|
+
return pkg;
|
|
734
|
+
}
|
|
735
|
+
function foreignProxies(root) {
|
|
736
|
+
const ecoreNsURI = EcorePackageImpl2.eINSTANCE.getNsURI();
|
|
737
|
+
const proxies = /* @__PURE__ */ new Set();
|
|
738
|
+
for (const obj of [root, ...root.eAllContents()]) {
|
|
739
|
+
for (const feature of obj.eClass().getEAllStructuralFeatures()) {
|
|
740
|
+
if (!isEReference(feature) || feature.isContainment()) continue;
|
|
741
|
+
const value = obj.eGet(feature);
|
|
742
|
+
const values = feature.isMany() ? [...value] : value ? [value] : [];
|
|
743
|
+
for (const v of values) {
|
|
744
|
+
if (v.eIsProxy() && getProxyURI2(v)?.trimFragment().toString() !== ecoreNsURI) proxies.add(v);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
694
747
|
}
|
|
695
|
-
return
|
|
748
|
+
return proxies;
|
|
696
749
|
}
|
|
697
750
|
|
|
698
751
|
// src/generate.ts
|
|
@@ -816,16 +869,26 @@ var TypeImportMapping = class _TypeImportMapping {
|
|
|
816
869
|
var generationContext = {
|
|
817
870
|
pkg: void 0,
|
|
818
871
|
typeImports: new TypeImportMapping(),
|
|
819
|
-
generateEcore: false
|
|
872
|
+
generateEcore: false,
|
|
873
|
+
packageImports: /* @__PURE__ */ new Map()
|
|
820
874
|
};
|
|
821
875
|
var GENERATE_ECORE_OPTION = "generate-ecore";
|
|
822
876
|
var TYPE_IMPORTS_OPTION = "type-imports";
|
|
877
|
+
var PACKAGE_IMPORTS_OPTION = "package-imports";
|
|
823
878
|
function beginGeneration(pkg, options) {
|
|
824
879
|
generationContext.pkg = pkg;
|
|
825
880
|
const entries = options[TYPE_IMPORTS_OPTION];
|
|
826
881
|
const valid = typeof entries === "object" && entries !== null && !Array.isArray(entries);
|
|
827
882
|
generationContext.typeImports = new TypeImportMapping(valid ? entries : {});
|
|
828
883
|
generationContext.generateEcore = options[GENERATE_ECORE_OPTION] === true;
|
|
884
|
+
generationContext.packageImports = new Map(
|
|
885
|
+
Object.entries(objectOption(options[PACKAGE_IMPORTS_OPTION])).filter(
|
|
886
|
+
(entry) => typeof entry[1] === "string" && entry[1] !== ""
|
|
887
|
+
)
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
function objectOption(value) {
|
|
891
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
829
892
|
}
|
|
830
893
|
function isExternal(classifier) {
|
|
831
894
|
const owner = classifier.getEPackage();
|
|
@@ -924,8 +987,10 @@ function locationOfPath(path) {
|
|
|
924
987
|
}
|
|
925
988
|
|
|
926
989
|
// src/typescript-filters.ts
|
|
990
|
+
import { posix as posix2 } from "path";
|
|
927
991
|
import {
|
|
928
992
|
detailValue as detailValue2,
|
|
993
|
+
EcorePackageImpl as EcorePackageImpl3,
|
|
929
994
|
isEDataType as isDataTypeObject
|
|
930
995
|
} from "@typemf/core";
|
|
931
996
|
var TYPEMF_GENERATOR_ANNOTATION_SOURCE = "https://typemf.dev/generator";
|
|
@@ -1074,8 +1139,60 @@ function tsBaseType(classifier) {
|
|
|
1074
1139
|
return "EObject";
|
|
1075
1140
|
}
|
|
1076
1141
|
if (isEDataType(classifier)) return tsPrimitiveType(classifier);
|
|
1142
|
+
if (isExternal(classifier)) {
|
|
1143
|
+
const name = classifier.getName();
|
|
1144
|
+
if (isEcoreClassifier(classifier)) importCollector.add({ name, location: "types", foundational: true });
|
|
1145
|
+
else
|
|
1146
|
+
importCollector.add({
|
|
1147
|
+
name,
|
|
1148
|
+
location: "types",
|
|
1149
|
+
foundational: false,
|
|
1150
|
+
from: otherPackageImport(classifier.getEPackage(), "types", name)
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1077
1153
|
return classifier.getName();
|
|
1078
1154
|
}
|
|
1155
|
+
function isEcoreClassifier(classifier) {
|
|
1156
|
+
return classifier.getEPackage()?.getNsURI() === EcorePackageImpl3.eINSTANCE.getNsURI();
|
|
1157
|
+
}
|
|
1158
|
+
function otherPackageImport(pkg, folder, file) {
|
|
1159
|
+
const nsURI = pkg.getNsURI() ?? "";
|
|
1160
|
+
const location = generationContext.packageImports.get(nsURI);
|
|
1161
|
+
if (location === void 0) {
|
|
1162
|
+
throw new Error(
|
|
1163
|
+
`The package '${nsURI}' is referenced, but the "${PACKAGE_IMPORTS_OPTION}" option has no entry for it.`
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
if (!location.startsWith("./") && !location.startsWith("../")) return location;
|
|
1167
|
+
const path = posix2.join(location, folder, `${file}.js`);
|
|
1168
|
+
return path.startsWith(".") ? path : `./${path}`;
|
|
1169
|
+
}
|
|
1170
|
+
function findOtherPackageSuperTypes(pkg) {
|
|
1171
|
+
const messages = [];
|
|
1172
|
+
for (const classifier of pkg.getEClassifiers()) {
|
|
1173
|
+
if (!isEClass2(classifier)) continue;
|
|
1174
|
+
for (const superType2 of classifier.getESuperTypes()) {
|
|
1175
|
+
if (superType2.getEPackage() !== pkg)
|
|
1176
|
+
messages.push(
|
|
1177
|
+
`${classifier.getName()}: the supertype ${superType2.getName()} belongs to another package, which is not supported yet`
|
|
1178
|
+
);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
return messages;
|
|
1182
|
+
}
|
|
1183
|
+
function otherPackageClassifierExpr(classifier) {
|
|
1184
|
+
const pkg = classifier.getEPackage();
|
|
1185
|
+
const implName = isEcoreClassifier(classifier) ? "EcorePackageImpl" : `${packageClassName(pkg)}Impl`;
|
|
1186
|
+
if (isEcoreClassifier(classifier)) importCollector.add({ name: implName, location: "impl", foundational: true });
|
|
1187
|
+
else
|
|
1188
|
+
importCollector.add({
|
|
1189
|
+
name: implName,
|
|
1190
|
+
location: "impl",
|
|
1191
|
+
foundational: false,
|
|
1192
|
+
from: otherPackageImport(pkg, "impl", implName)
|
|
1193
|
+
});
|
|
1194
|
+
return `${implName}.eINSTANCE.get${classifier.getName()}()`;
|
|
1195
|
+
}
|
|
1079
1196
|
function resolveDataTypeTs(dataType) {
|
|
1080
1197
|
const resolved = resolveDataTypeText(dataType);
|
|
1081
1198
|
const importInfo = generationContext.typeImports.lookup(dataType);
|
|
@@ -1170,7 +1287,7 @@ function isEEnum(classifier) {
|
|
|
1170
1287
|
function isEDataType(classifier) {
|
|
1171
1288
|
return !isEClass2(classifier) && !isEEnum(classifier);
|
|
1172
1289
|
}
|
|
1173
|
-
function
|
|
1290
|
+
function isEReference2(feature) {
|
|
1174
1291
|
return "isContainment" in feature;
|
|
1175
1292
|
}
|
|
1176
1293
|
function paramList(operation) {
|
|
@@ -1487,9 +1604,10 @@ function referencedApiTypes(features, ...excludeTypeNames) {
|
|
|
1487
1604
|
const names = /* @__PURE__ */ new Set();
|
|
1488
1605
|
for (const feature of features) {
|
|
1489
1606
|
const type = feature.getEType();
|
|
1490
|
-
if (type && (
|
|
1607
|
+
if (type && (isEReference2(feature) || isEEnum(type)) && !isExternal(type) && !exclude.has(type.getName()))
|
|
1608
|
+
names.add(type.getName());
|
|
1491
1609
|
for (const argument of genericArgumentClassifiers(feature.getEGenericType())) {
|
|
1492
|
-
if ((isEClass2(argument) || isEEnum(argument)) && !exclude.has(argument.getName()))
|
|
1610
|
+
if ((isEClass2(argument) || isEEnum(argument)) && !isExternal(argument) && !exclude.has(argument.getName()))
|
|
1493
1611
|
names.add(argument.getName());
|
|
1494
1612
|
}
|
|
1495
1613
|
}
|
|
@@ -1499,7 +1617,7 @@ function referencedOperationTypes(operations, ...excludeTypeNames) {
|
|
|
1499
1617
|
const exclude = new Set(excludeTypeNames);
|
|
1500
1618
|
const names = /* @__PURE__ */ new Set();
|
|
1501
1619
|
const maybeAdd = (type) => {
|
|
1502
|
-
if (type && (isEClass2(type) || isEEnum(type)) && !exclude.has(type.getName())) {
|
|
1620
|
+
if (type && (isEClass2(type) || isEEnum(type)) && !isExternal(type) && !exclude.has(type.getName())) {
|
|
1503
1621
|
names.add(type.getName());
|
|
1504
1622
|
}
|
|
1505
1623
|
};
|
|
@@ -1562,7 +1680,8 @@ var typescriptTemplateSet = {
|
|
|
1562
1680
|
...findUnnamedElements(pkg),
|
|
1563
1681
|
...findUnresolvedCollisions(pkg).map(
|
|
1564
1682
|
(c) => `${c.classifierName}.${c.memberName}: name collision between ${c.sources.join(" and ")}`
|
|
1565
|
-
)
|
|
1683
|
+
),
|
|
1684
|
+
...findOtherPackageSuperTypes(pkg)
|
|
1566
1685
|
];
|
|
1567
1686
|
},
|
|
1568
1687
|
configureEnvironment(env, { pkg, options }) {
|
|
@@ -1584,7 +1703,7 @@ var typescriptTemplateSet = {
|
|
|
1584
1703
|
env.addGlobal("isEClass", isEClass2);
|
|
1585
1704
|
env.addGlobal("isEEnum", isEEnum);
|
|
1586
1705
|
env.addGlobal("isEDataType", isEDataType);
|
|
1587
|
-
env.addGlobal("isEReference",
|
|
1706
|
+
env.addGlobal("isEReference", isEReference2);
|
|
1588
1707
|
env.addGlobal("paramList", paramList);
|
|
1589
1708
|
env.addGlobal("argList", argList);
|
|
1590
1709
|
env.addGlobal("ucfirst", ucfirst);
|
|
@@ -1616,6 +1735,7 @@ var typescriptTemplateSet = {
|
|
|
1616
1735
|
env.addGlobal("metaclassAccessorCollision", metaclassAccessorCollision);
|
|
1617
1736
|
env.addGlobal("metaclassAccessorCollisionTypeNames", metaclassAccessorCollisionTypeNames);
|
|
1618
1737
|
env.addGlobal("optionalParamList", optionalParamList);
|
|
1738
|
+
env.addGlobal("otherPackageClassifierExpr", otherPackageClassifierExpr);
|
|
1619
1739
|
env.addGlobal("typeGuardsClassName", typeGuardsClassName);
|
|
1620
1740
|
env.addGlobal("useImport", useImport);
|
|
1621
1741
|
},
|
|
@@ -1676,6 +1796,7 @@ export {
|
|
|
1676
1796
|
featureSetter,
|
|
1677
1797
|
findEClassByName,
|
|
1678
1798
|
findFeatureAnnotationProblems,
|
|
1799
|
+
findOtherPackageSuperTypes,
|
|
1679
1800
|
findUnnamedElements,
|
|
1680
1801
|
findUnresolvedCollisions,
|
|
1681
1802
|
generate,
|
|
@@ -1687,7 +1808,7 @@ export {
|
|
|
1687
1808
|
isEClass2 as isEClass,
|
|
1688
1809
|
isEDataType,
|
|
1689
1810
|
isEEnum,
|
|
1690
|
-
isEReference,
|
|
1811
|
+
isEReference2 as isEReference,
|
|
1691
1812
|
isOperationDerived,
|
|
1692
1813
|
isOptional,
|
|
1693
1814
|
isPrimitiveValueType,
|
|
@@ -1704,6 +1825,7 @@ export {
|
|
|
1704
1825
|
needsImplOnlySetter,
|
|
1705
1826
|
operationBody,
|
|
1706
1827
|
optionalParamList,
|
|
1828
|
+
otherPackageClassifierExpr,
|
|
1707
1829
|
overloadDispatch,
|
|
1708
1830
|
packageClassName,
|
|
1709
1831
|
paramList,
|