grantila / typeconv

Convert between JSON Schema, TypeScript, GraphQL, Open API and SureType
MIT License
424 stars 8 forks source link

[ts-to-oapi] Enums are not generated during conversion. #28

Open 0xOmarA opened 2 years ago

0xOmarA commented 2 years ago

Hi. I have a number of TypeScript files which I am trying to generate OpenAPI documentation for using this tool. In one of the typescript files, I have an enum which looks like the following:

export enum SpeciesKind {
    Human,
    Bird,
    Fish
}

I am using the following command to generate OpenAPI documentation from my typescript files:

npx typeconv -f ts -t oapi -o spec ./src/*.ts

When running the above command, everything is generated as expected except for any enums I have in my code, no OpenAPI schema is generated for them.

jkrauss82 commented 2 years ago

Just found out about this issue, can confirm enums are not generated. Would be great if this can be fixed!

erkin98 commented 2 years ago

Annoying issue

MattHalloran commented 1 year ago

I was having this issue as well. First, I tried replacing each enum with a string union like this:

    // Read the TypeScript file
    let tsData = fs.readFileSync(pathToTsFile, "utf8");

    // Split into blocks of types
    let blocks = tsData.split("\n\n");

    // Convert TypeScript enums to string unions
    tsData = tsData.replace(/export enum (\w+) \{([^}]+)\}/g, (match, enumName, enumBody) => {
        // Split the body of the enum into individual members
        const enumMembers = enumBody.split(",");

        // Map each member to its value (the part after the equals sign)
        const enumValues = enumMembers.map(member => {
            const parts = member.trim().split("=");
            return parts[1]?.trim() || parts[0]?.trim();
        });

        // Join the values into a union
        const union = enumValues.join(" | ");

        // Return the replacement
        const result = `export type ${enumName} = ${union};`;
        console.log(result);
        return result;
    });

    // Join the blocks back together
    tsData = blocks.join("\n\n");

Unfortunately, this didn't work. It seems referencing a string union isn't enough. What I had to do instead was replace each reference to an enum with its string union directly, like this:

    // Read the TypeScript file
    let tsData = fs.readFileSync(pathToTsFile, "utf8");

    // Split into blocks of types
    let blocks = tsData.split("\n\n");

    // Define a Map to store the enums and their corresponding string unions
    const enumMap = new Map<string, string>();

    // Convert TypeScript enums to string unions
    tsData = tsData.replace(/export enum (\w+) \{([^}]+)\}/g, (match, enumName, enumBody) => {
        // Split the body of the enum into individual members
        const enumMembers = enumBody.split(",");

        // Map each member to its value (the part after the equals sign)
        const enumValues = enumMembers.map(member => {
            const parts = member.trim().split("=");
            return parts[1]?.trim() || parts[0]?.trim();
        });

        // Join the values into a union
        const union = enumValues.join(" | ");

        // Store the enum and its corresponding union in the map
        enumMap.set(enumName, union);

        // Return an empty string to remove the enum block
        return "";
    });

    // Remove enum blocks from the array of blocks
    blocks = blocks.filter(block => !block.startsWith("export enum "));

    // Replace enum usages with their corresponding unions
    blocks = blocks.map(block => {
        let replacedBlock = block;
        for (const [enumName, union] of enumMap.entries()) {
            const enumRegex = new RegExp(`\\b${enumName}\\b`, "g");
            replacedBlock = replacedBlock.replace(enumRegex, union);
        }
        return replacedBlock;
    });

    // Join the blocks back together
    tsData = blocks.join("\n\n");