function getMetaProperties()

in packages/xml-parser-ts-codegen/src/codegen.ts [568:876]


function getMetaProperties(
  __RELATIVE_LOCATION: string,
  __META_TYPE_MAPPING: Map<string, XptcMetaType>,
  __GLOBAL_ELEMENTS: Map<string, XptcElement>,
  __SUBSTITUTIONS: Map<string, Map<string, string[]>>,
  __XSDS: Map<string, XsdSchema>,
  __NAMED_TYPES_BY_TS_NAME: Map<string, XptcComplexType | XptcSimpleType>,
  ct: XptcComplexType,
  metaTypeName: string
): { anonymousTypes: XptcMetaType[]; needsExtensionType: boolean; metaProperties: XptcMetaTypeProperty[] } {
  /** Accumulates all properties of this complex type (ct). Attributes and elements. */
  let ctMetaProperties: XptcMetaTypeProperty[] = [];

  /** Accumulates all anonymous types instantiated on this complex type's hierarchy */
  const anonymousTypes: XptcMetaType[] = [];

  const immediateParentType = ct.childOf
    ? getTsTypeFromLocalRef(__XSDS, __NAMED_TYPES_BY_TS_NAME, ct.declaredAtRelativeLocation, ct.childOf)
    : undefined;

  let curParentCt = immediateParentType ? __NAMED_TYPES_BY_TS_NAME.get(immediateParentType.name) : undefined;

  let needsExtensionType = ct.needsExtensionType;

  while (curParentCt) {
    const curParentCtMetaProperties: XptcMetaTypeProperty[] = [];
    if (curParentCt?.type === "complex") {
      const curParentCtMetaTypeName = getTsNameFromNamedType(
        curParentCt.declaredAtRelativeLocation,
        curParentCt.isAnonymous ? getAnonymousMetaTypeName(curParentCt.forElementWithName, "GLOBAL") : curParentCt.name
      );
      needsExtensionType = needsExtensionType || curParentCt.needsExtensionType;
      if (curParentCt.isAnonymous) {
        throw new Error("Anonymous types are never parent types.");
      }

      for (const a of curParentCt.attributes) {
        const attributeType = getTsTypeFromLocalRef(
          __XSDS,
          __NAMED_TYPES_BY_TS_NAME,
          curParentCt.declaredAtRelativeLocation,
          a.localTypeRef
        );
        if (!attributeType) {
          throw new Error(`Can't resolve local type ref ${a.localTypeRef}`);
        }

        curParentCtMetaProperties.push({
          declaredAt: curParentCt.declaredAtRelativeLocation,
          fromType: curParentCtMetaTypeName,
          elem: undefined,
          name: `@_${a.name}`,
          metaType: { name: getMetaTypeName(attributeType), xsdType: attributeType.annotation },
          isArray: false,
          isOptional: a.isOptional,
        });
      }

      for (const e of curParentCt.elements) {
        if (e.kind === "ofAnonymousType") {
          const anonymousTypeName = getAnonymousMetaTypeName(e.name, metaTypeName);
          const mp = getMetaProperties(
            __RELATIVE_LOCATION,
            __META_TYPE_MAPPING,
            __GLOBAL_ELEMENTS,
            __SUBSTITUTIONS,
            __XSDS,
            __NAMED_TYPES_BY_TS_NAME,
            e.anonymousType,
            anonymousTypeName
          );
          anonymousTypes.push({ name: anonymousTypeName, properties: mp.metaProperties });
          anonymousTypes.push(...mp.anonymousTypes);
          __META_TYPE_MAPPING.set(anonymousTypeName, {
            name: anonymousTypeName,
            properties: mp.metaProperties,
          });
          curParentCtMetaProperties.push({
            elem: undefined, // REALLY?
            declaredAt: curParentCt.declaredAtRelativeLocation,
            fromType: curParentCtMetaTypeName,
            name: e.name,
            metaType: { name: anonymousTypeName, xsdType: "Anonumous type..." },
            isArray: e.isArray,
            isOptional: e.isOptional,
          });
        } else if (e.kind === "ofNamedType") {
          const tsType = getTsTypeFromLocalRef(
            __XSDS,
            __NAMED_TYPES_BY_TS_NAME,
            ct.declaredAtRelativeLocation,
            e.typeName
          );

          curParentCtMetaProperties.push({
            declaredAt: curParentCt.declaredAtRelativeLocation,
            fromType: curParentCtMetaTypeName,
            elem: undefined, // REALLY?
            name: e.name,
            metaType: { name: getMetaTypeName(tsType), xsdType: tsType.annotation },
            typeBody: getTsTypeBody(tsType),
            isArray: e.isArray,
            isOptional: e.isOptional,
          });
        } else if (e.kind === "ofRef") {
          const referencedElement = getXptcElementFromLocalElementRef(
            __XSDS,
            __GLOBAL_ELEMENTS,
            ct.declaredAtRelativeLocation,
            e.ref
          );

          if (!referencedElement) {
            throw new Error(`Can't find reference to element '${e.ref}'`);
          }

          const tsType = referencedElement.type
            ? getTsTypeFromLocalRef(
                __XSDS,
                __NAMED_TYPES_BY_TS_NAME,
                ct.declaredAtRelativeLocation,
                referencedElement.type
              )
            : {
                name: getTsNameFromNamedType(
                  ct.declaredAtRelativeLocation,
                  getAnonymousMetaTypeName(referencedElement.name, "GLOBAL")
                ),
                annotation: "Anonymous type from element " + referencedElement.name,
              };

          curParentCtMetaProperties.push({
            declaredAt: referencedElement?.declaredAtRelativeLocation,
            fromType: ct.isAnonymous ? "" : curParentCtMetaTypeName,
            name: referencedElement.name,
            elem: referencedElement,
            metaType: { name: getMetaTypeName(tsType), xsdType: tsType.annotation },
            typeBody: () =>
              getTypeBodyForElementRef(
                __RELATIVE_LOCATION,
                __META_TYPE_MAPPING,
                __GLOBAL_ELEMENTS,
                __SUBSTITUTIONS,
                __XSDS,
                __NAMED_TYPES_BY_TS_NAME,
                ct,
                referencedElement
              ),
            isArray: e.isArray,
            isOptional: e.isOptional,
          });
        } else {
          throw new Error("Unknonwn type of element " + e);
        }
      }

      const nextParentType = curParentCt.childOf
        ? getTsTypeFromLocalRef(
            __XSDS,
            __NAMED_TYPES_BY_TS_NAME,
            curParentCt.declaredAtRelativeLocation,
            curParentCt.childOf
          )
        : undefined;

      // Make sure the inheritance order is respected. Elements should be listed always from the most generic to the most specific type.
      // Since we're iterating upwards in the hierarchy, we need to invert prepend the array with the props we find on each step of the hierarchy.
      ctMetaProperties = [...curParentCtMetaProperties, ...ctMetaProperties];
      curParentCt = nextParentType ? __NAMED_TYPES_BY_TS_NAME.get(nextParentType.name) : undefined;
    } else if (curParentCt?.type === "simple") {
      throw new Error("Can't have a non-complex type as parent of another.");
    } else {
      curParentCt = undefined;
    }
  }

  // Own properties are parsed later to ensure xsd:sequence order.

  for (const a of ct.attributes) {
    const attributeType = getTsTypeFromLocalRef(
      __XSDS,
      __NAMED_TYPES_BY_TS_NAME,
      ct.declaredAtRelativeLocation,
      a.localTypeRef
    );

    ctMetaProperties.push({
      declaredAt: ct.declaredAtRelativeLocation,
      fromType: metaTypeName,
      name: `@_${a.name}`,
      elem: undefined,
      metaType: { name: getMetaTypeName(attributeType), xsdType: attributeType.annotation },
      isArray: false,
      isOptional: a.isOptional,
    });
  }

  for (const e of ct.elements) {
    if (e.kind === "ofRef") {
      const referencedElement = getXptcElementFromLocalElementRef(
        __XSDS,
        __GLOBAL_ELEMENTS,
        ct.declaredAtRelativeLocation,
        e.ref
      );

      if (!referencedElement) {
        throw new Error(`Can't find reference to element '${e.ref}'`);
      }

      const tsType = referencedElement.type
        ? getTsTypeFromLocalRef(__XSDS, __NAMED_TYPES_BY_TS_NAME, ct.declaredAtRelativeLocation, referencedElement.type)
        : {
            name: getTsNameFromNamedType(
              ct.declaredAtRelativeLocation,
              getAnonymousMetaTypeName(referencedElement.name, "GLOBAL")
            ),
            annotation: "Anonymous type from element " + referencedElement.name,
          };

      ctMetaProperties.push({
        declaredAt: referencedElement?.declaredAtRelativeLocation,
        fromType: ct.isAnonymous ? "" : metaTypeName,
        name: referencedElement.name,
        elem: referencedElement,
        metaType: { name: getMetaTypeName(tsType), xsdType: tsType.annotation },
        typeBody: () =>
          getTypeBodyForElementRef(
            __RELATIVE_LOCATION,
            __META_TYPE_MAPPING,
            __GLOBAL_ELEMENTS,
            __SUBSTITUTIONS,
            __XSDS,
            __NAMED_TYPES_BY_TS_NAME,
            ct,
            referencedElement
          ),
        isArray: e.isArray,
        isOptional: e.isOptional,
      });
    } else if (e.kind === "ofNamedType") {
      const tsType = getTsTypeFromLocalRef(__XSDS, __NAMED_TYPES_BY_TS_NAME, ct.declaredAtRelativeLocation, e.typeName);
      ctMetaProperties.push({
        declaredAt: ct.declaredAtRelativeLocation,
        fromType: metaTypeName,
        name: e.name,
        elem: undefined, // REALLY?
        metaType: { name: getMetaTypeName(tsType), xsdType: tsType.annotation },
        typeBody: getTsTypeBody(tsType),
        isArray: e.isArray,
        isOptional: e.isOptional,
      });
    } else if (e.kind === "ofAnonymousType") {
      const anonymousTypeName = getAnonymousMetaTypeName(e.name, metaTypeName);
      const mp = getMetaProperties(
        __RELATIVE_LOCATION,
        __META_TYPE_MAPPING,
        __GLOBAL_ELEMENTS,
        __SUBSTITUTIONS,
        __XSDS,
        __NAMED_TYPES_BY_TS_NAME,
        e.anonymousType,
        anonymousTypeName
      );
      anonymousTypes.push({ name: anonymousTypeName, properties: mp.metaProperties });
      anonymousTypes.push(...mp.anonymousTypes);
      __META_TYPE_MAPPING.set(anonymousTypeName, {
        name: anonymousTypeName,
        properties: mp.metaProperties,
      });
      ctMetaProperties.push({
        declaredAt: ct.declaredAtRelativeLocation,
        fromType: metaTypeName,
        name: e.name,
        elem: undefined, // REALLY?
        metaType: { name: anonymousTypeName, xsdType: "Anonymous type..." },
        isArray: e.isArray,
        isOptional: e.isOptional,
      });
    } else {
      throw new Error(`Unknown kind of XptcComplexType '${e}'`);
    }
  }

  if (ct.isSimpleContent && ct.childOf) {
    const t = getTsTypeFromLocalRef(__XSDS, __NAMED_TYPES_BY_TS_NAME, ct.declaredAtRelativeLocation, ct.childOf);
    ctMetaProperties.push({
      declaredAt: ct.declaredAtRelativeLocation,
      fromType: metaTypeName,
      name: `__$$text`,
      elem: undefined,
      metaType: {
        name: t.name,
        xsdType: t.annotation,
      },
      isArray: false,
      isOptional: false,
    });
  }

  if (!(ct.type === "complex" && !ct.isAnonymous && ct.isAbstract)) {
    __META_TYPE_MAPPING.set(metaTypeName, {
      name: metaTypeName,
      properties: [...ctMetaProperties.reduce((acc, p) => acc.set(p.name, p), new Map()).values()], // Removing duplicates.
    });
  }

  return { metaProperties: ctMetaProperties, needsExtensionType, anonymousTypes };
}