async function main()

in packages/xml-parser-ts-codegen/src/codegen.ts [126:485]


async function main() {
  const __LOCATION = process.argv[2];
  const __ROOT_ELEMENT_NAME = process.argv[3];
  const __BASE_LOCATION = path.dirname(__LOCATION);
  const __RELATIVE_LOCATION = path.basename(__LOCATION);

  const __ROOT_ELEMENT = `${__RELATIVE_LOCATION}__${__ROOT_ELEMENT_NAME}`;

  const __RELATIVE_LOCATION_WITHOUT_EXTENSION = __RELATIVE_LOCATION.replace(path.extname(__RELATIVE_LOCATION), "");

  const __CONVENTIONS = {
    outputFileForGeneratedTypes: path.resolve(".", path.join(__BASE_LOCATION, "ts-gen/types.ts")),
    outputFileForGeneratedMeta: path.resolve(".", path.join(__BASE_LOCATION, "ts-gen/meta.ts")),
  };

  // gather all the XSDs
  const __XSDS = new Map<string, XsdSchema>(await parseDeep(__XSD_PARSER, __BASE_LOCATION, __RELATIVE_LOCATION));

  // // process <xsd:simpleType>'s
  const __SIMPLE_TYPES: XptcSimpleType[] = Array.from(__XSDS.entries()).flatMap(([location, schema]) =>
    (schema["xsd:schema"]["xsd:simpleType"] || []).flatMap((s) => {
      if (s["xsd:union"]) {
        if (s["xsd:union"]["@_memberTypes"] === "xsd:anyURI") {
          return [
            {
              comment: "xsd:anyURI",
              type: "simple",
              kind: "enum",
              name: s["@_name"] ?? s["@_name"],
              declaredAtRelativeLocation: location,
              values: [],
            },
          ];
        }
        return (s["xsd:union"]["xsd:simpleType"] ?? []).flatMap((ss) =>
          xsdSimpleTypeToXptcSimpleType(ss, location, s["@_name"])
        );
      } else {
        return xsdSimpleTypeToXptcSimpleType(s, location, s["@_name"]);
      }
    })
  );

  // // process <xsd:complexType>'s
  const __COMPLEX_TYPES: XptcComplexType[] = [];
  for (const [location, xsd] of __XSDS.entries()) {
    for (const xsdCt of xsd["xsd:schema"]["xsd:complexType"] || []) {
      const isAbstract = xsdCt["@_abstract"] ?? false;
      const extensionElement =
        xsdCt["xsd:complexContent"]?.["xsd:extension"] ?? xsdCt["xsd:simpleContent"]?.["xsd:extension"];

      __COMPLEX_TYPES.push({
        type: "complex",
        comment: isAbstract ? "abstract" : "",
        isAbstract,
        isAnonymous: false,
        name: xsdCt["@_name"]!,
        isSimpleContent: !!xsdCt["xsd:simpleContent"],
        needsExtensionType: !!xsdCt["xsd:anyAttribute"] || !!xsdCt["xsd:sequence"]?.["xsd:any"],
        declaredAtRelativeLocation: location,
        childOf: extensionElement?.["@_base"],
        elements: [
          ...(xsdCt["xsd:all"]?.["xsd:element"] ?? []).map((s) =>
            xsdElementToXptcElement(xsdCt["@_name"]!, s, location)
          ),
          ...(xsdCt["xsd:sequence"]?.["xsd:element"] ?? []).map((s) =>
            xsdElementToXptcElement(xsdCt["@_name"]!, s, location)
          ),
          ...(extensionElement?.["xsd:sequence"]?.["xsd:element"] ?? []).map((s) =>
            xsdElementToXptcElement(xsdCt["@_name"]!, s, location)
          ),
          ...(extensionElement?.["xsd:sequence"]?.["xsd:choice"]?.["xsd:element"] ?? []).map((s) =>
            xsdElementToXptcElement(xsdCt["@_name"]!, s, location, { forceOptional: true })
          ),
          ...(extensionElement?.["xsd:choice"]?.["xsd:element"] ?? []).map((s) =>
            xsdElementToXptcElement(xsdCt["@_name"]!, s, location, { forceOptional: true })
          ),
          ...(extensionElement?.["xsd:choice"]?.["xsd:sequence"]?.["xsd:element"] ?? []).map((s) =>
            xsdElementToXptcElement(xsdCt["@_name"]!, s, location, { forceOptional: true })
          ),
        ],
        attributes: [
          ...(xsdCt["xsd:attribute"] ?? []).map((a) => xsdAttributeToXptcAttribute(a)),
          ...(extensionElement?.["xsd:attribute"] ?? []).map((a) => xsdAttributeToXptcAttribute(a)),
        ],
      });
    }
  }

  // // process <xsd:element>'s
  const __GLOBAL_ELEMENTS = new Map<string, XptcElement>();
  for (const [location, xsd] of __XSDS.entries()) {
    for (const e of xsd["xsd:schema"]["xsd:element"] || []) {
      const a = xsdElementToXptcElement("GLOBAL", { ...e, "@_minOccurs": 0, "@_maxOccurs": "unbounded" }, location, {
        forceOptional: false,
      });

      __GLOBAL_ELEMENTS.set(`${location}__${e["@_name"]}`, {
        name: e["@_name"],
        isAbstract: e["@_abstract"] ?? false,
        substitutionGroup: e["@_substitutionGroup"],
        type: e["@_type"],
        declaredAtRelativeLocation: location,
        anonymousType: a.kind === "ofAnonymousType" ? a.anonymousType : undefined,
      });
    }
  }

  // // substitutionGroups are SCOPED. Meaning that we need to consider only what the current XSD is importing into it.
  // // This map goes from a relativeLocation to an elementName to a list of elementNames.
  const __SUBSTITUTIONS = new Map<string, Map<string, string[]>>();
  for (const [baseLoc, _] of __XSDS.entries()) {
    const xsds = new Map<string, XsdSchema>(await parseDeep(__XSD_PARSER, __BASE_LOCATION, baseLoc));

    for (const [xLocation, xsd] of xsds.entries()) {
      const localizedSubstitutions = __SUBSTITUTIONS.get(xLocation) ?? new Map<string, string[]>();
      __SUBSTITUTIONS.set(xLocation, localizedSubstitutions);
      for (const e of xsd["xsd:schema"]["xsd:element"] || []) {
        if (e["@_substitutionGroup"]) {
          const subsGroup = getXptcElementFromLocalElementRef(
            __XSDS,
            __GLOBAL_ELEMENTS,
            xLocation,
            e["@_substitutionGroup"]
          );
          if (!subsGroup) {
            throw new Error(`Invalid subsitution group for element '${e["@_name"]}'`);
          }
          const elem = getXptcElementFromLocalElementRef(__XSDS, __GLOBAL_ELEMENTS, xLocation, e["@_name"]);
          if (!elem) {
            throw new Error(`Invalid element '${e["@_name"]}'`);
          }

          const localizedElementName = `${subsGroup.declaredAtRelativeLocation}__${subsGroup.name}`;

          // Using this strategy to remove duplicates.
          const accumulatedSubstitutionElements = new Set([
            ...(localizedSubstitutions.get(localizedElementName) ?? []),
            `${xLocation}__${elem.name}`,
          ]);

          localizedSubstitutions.set(localizedElementName, [...accumulatedSubstitutionElements]);
        }
      }
    }
  }

  Array.from(__GLOBAL_ELEMENTS.values()).forEach((e) => {
    if (!e.anonymousType) {
      return;
    } else {
      __COMPLEX_TYPES.push(e.anonymousType);
    }
  });

  const __NAMED_TYPES_BY_TS_NAME = new Map<string, XptcComplexType | XptcSimpleType>([
    ...__SIMPLE_TYPES.map(
      (st) => [getTsNameFromNamedType(st.declaredAtRelativeLocation, st.name), st] as [string, XptcSimpleType]
    ),
    ...__COMPLEX_TYPES.map((ct) => {
      if (ct.isAnonymous) {
        const name = getAnonymousMetaTypeName(ct.forElementWithName, "GLOBAL");
        return [getTsNameFromNamedType(ct.declaredAtRelativeLocation, name), ct] as [string, XptcComplexType];
      } else {
        return [getTsNameFromNamedType(ct.declaredAtRelativeLocation, ct.name), ct] as [string, XptcComplexType];
      }
    }),
  ]);

  const __META_TYPE_MAPPING = new Map<string, XptcMetaType>();

  const rootTsTypeName = getTsNameFromNamedType(
    __RELATIVE_LOCATION_WITHOUT_EXTENSION,
    __GLOBAL_ELEMENTS.get(__ROOT_ELEMENT)!.type ?? getAnonymousMetaTypeName(__ROOT_ELEMENT_NAME, "GLOBAL")
  );

  let ts = "";

  for (const sp of __SIMPLE_TYPES) {
    if (sp.kind === "int") {
      // ignore int types, they're only interesting for validation.
      continue;
    }

    const enumName = getTsNameFromNamedType(sp.declaredAtRelativeLocation, sp.name);
    if (sp.comment === "xsd:anyURI") {
      ts += `
export type ${enumName} = string; // ${sp.comment}
`;
    } else if (sp.kind === "enum") {
      ts += `
export type ${enumName} = |
${sp.values.map((v) => `    '${v}'`).join(" |\n")}
`;
    }
  }

  for (const ct of __COMPLEX_TYPES) {
    const typeName = getTsNameFromNamedType(
      ct.declaredAtRelativeLocation,
      ct.isAnonymous ? getAnonymousMetaTypeName(ct.forElementWithName, "GLOBAL") : ct.name
    );

    const { metaProperties, needsExtensionType, anonymousTypes } = getMetaProperties(
      __RELATIVE_LOCATION,
      __META_TYPE_MAPPING,
      __GLOBAL_ELEMENTS,
      __SUBSTITUTIONS,
      __XSDS,
      __NAMED_TYPES_BY_TS_NAME,
      ct,
      typeName
    );

    const properties = metaProperties
      .map((p) => {
        const optionalMarker = p.isOptional ? "?" : "";
        const arrayMarker = p.isArray ? "[]" : "";
        const tsType =
          p.metaType.name === "integer" || p.metaType.name === "float" || p.metaType.name === "long"
            ? "number"
            : p.metaType.name;
        const ns = getMetaPropertyNs(__RELATIVE_LOCATION, p);
        return `    "${ns}${p.name}"${optionalMarker}: ${p.typeBody?.(tsType) ?? tsType}${arrayMarker}; // from type ${
          p.fromType
        } @ ${p.declaredAt}`;
      })
      .join("\n");

    const doc = ct.comment.trim() ? `/* ${ct.comment} */` : "";

    const anonymousTypesString = anonymousTypes
      .map((anonType) => {
        const anonymousTypesProperties = anonType.properties.map(
          (p) =>
            `    "${p.name}": ${
              p.metaType.name === "integer" || p.metaType.name === "float" || p.metaType.name === "long"
                ? "number"
                : p.metaType.name
            };`
        );

        // FIXME: Tiago: Not all anonymous types are extensible!
        return `export interface ${anonType.name} {
    __?: undefined;
${anonymousTypesProperties.join("\n")}
}`;
      })
      .join("\n");

    if (needsExtensionType) {
      const rootElementBaseType = rootTsTypeName === typeName ? "extends XmlParserTsRootElementBaseType" : "";
      ts += `
export interface ${typeName} ${rootElementBaseType} ${doc} {
    __?: undefined;
${properties}
}

${anonymousTypesString}
`;
    } else {
      const rootElementBaseType = rootTsTypeName === typeName ? "XmlParserTsRootElementBaseType & " : "";
      ts += `
export type ${typeName} = ${rootElementBaseType} ${doc} {
${properties}
}

${anonymousTypesString}
`;
    }
  }

  ts = `import { XmlParserTsRootElementBaseType } from "@kie-tools/xml-parser-ts"

  ${ts}
  `;

  ts = `// This file was automatically generated
  
${ts}
  `;

  fs.mkdirSync(path.dirname(__CONVENTIONS.outputFileForGeneratedTypes), { recursive: true });
  fs.writeFileSync(__CONVENTIONS.outputFileForGeneratedTypes, ts);

  // meta

  let meta = `
export const root = {
    element: "${getRealtiveLocationNs(__RELATIVE_LOCATION, __RELATIVE_LOCATION) + __ROOT_ELEMENT_NAME}",
    type: "${rootTsTypeName}" 
} as const;

export const ns = new Map<string, string>([
${[...__XSDS.entries()]
  .map(([k, v]) => {
    const uri = v["xsd:schema"]["@_targetNamespace"];
    const ns = getRealtiveLocationNs(__RELATIVE_LOCATION, k);
    return `    ["${uri}", "${ns}"],
    ["${ns}", "${uri}"],`;
  })
  .join("\n")}
]);

export const subs = {
${Array.from(__SUBSTITUTIONS.entries())
  .map(
    ([namespace, subs]) => `  "${getRealtiveLocationNs(__RELATIVE_LOCATION, namespace)}": {
${Array.from(subs.entries())
  .map(
    ([head, elements]) =>
      `${elements
        .map((e) => {
          const elementName = `${getRealtiveLocationNs(__RELATIVE_LOCATION, e.split("__")[0]) + e.split("__")[1]}`;
          const headName = `${getRealtiveLocationNs(__RELATIVE_LOCATION, head.split("__")[0]) + head.split("__")[1]}`;
          return `    "${elementName}": "${headName}",`;
        })
        .join("\n")}`
  )
  .join("\n")}
  },`
  )
  .join("\n")}
};

export const elements = {
${Array.from(__GLOBAL_ELEMENTS.entries())
  .map(([k, v]) => {
    const s = v.type?.split(":") || [getAnonymousMetaTypeName(v.name, "GLOBAL")];
    const elementName = `${getRealtiveLocationNs(__RELATIVE_LOCATION, k.split("__")[0])}${v.name}`;
    const elementType = `${getTsNameFromNamedType(v.declaredAtRelativeLocation, s.length === 1 ? s[0] : s[1])}`;
    return `  "${elementName}": "${elementType}",`;
  })
  .join("\n")}
};

export const meta = {
`;

  Array.from(__META_TYPE_MAPPING.entries()).forEach(([name, type]) => {
    meta += `    "${name}": {
`;
    type.properties.forEach((p) => {
      const ns = getMetaPropertyNs(__RELATIVE_LOCATION, p);
      meta += `        "${ns}${p.name}": { type: "${p.metaType.name}", isArray: ${p.isArray}, fromType: "${p.fromType}", xsdType: "${p.metaType.xsdType}" },
`;
    });

    meta += `    },
`;
  });

  meta += `} as const;
`;

  fs.mkdirSync(path.dirname(__CONVENTIONS.outputFileForGeneratedMeta), { recursive: true });
  fs.writeFileSync(__CONVENTIONS.outputFileForGeneratedMeta, meta);

  console.log(__LOGS.done(__LOCATION));
}