@typemf/generator 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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 { EcorePackageImpl as EcorePackageImpl2, ResourceSetImpl, URI } from "@typemf/core";
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 Ecore are supported.`
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
- const roots = resource.getContents().toArray();
696
- const dynamicPkg = roots[0];
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(`'${ecoreFilePath}' has no root element - expected a single EPackage.`);
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 convertDynamicEcoreToTyped(dynamicPkg);
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 isEReference(feature) {
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 && (isEReference(feature) || isEEnum(type)) && !exclude.has(type.getName())) names.add(type.getName());
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", 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, EClassifier, EDataType, EEnum, ETypedElement } from '@typemf/core';
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` (an EClass or
659
- * EEnum's own attribute/reference types) that need their own import
660
- * statement - EDataType primitives (string/number/boolean/Date) never do.
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 { EcorePackageImpl as EcorePackageImpl2, ResourceSetImpl, URI } from "@typemf/core";
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 Ecore are supported.`
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
- const roots = resource.getContents().toArray();
691
- const dynamicPkg = roots[0];
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(`'${ecoreFilePath}' has no root element - expected a single EPackage.`);
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 convertDynamicEcoreToTyped(dynamicPkg);
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 isEReference(feature) {
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 && (isEReference(feature) || isEEnum(type)) && !exclude.has(type.getName())) names.add(type.getName());
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", 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,