Skip to content

Latest commit

 

History

History
145 lines (108 loc) · 4.81 KB

CustomRenderer.md

File metadata and controls

145 lines (108 loc) · 4.81 KB

Extending quicktype functionality with a Custom Renderer

quicktype Interface

To customise your rendering output, you can extend existing quicktype classes and override existing methods to achieve the behaviour you want.

This process requires 3 main steps:

  1. Extending a Renderer Class
  2. Wrapping your Renderer in a TargetLanguage Class
  3. Using your new classes in the quicktype function
  4. Advanced Usage: Creating an entirely new Language

Creating a custom Renderer

Adding custom render logic for an existing language often involves extending a Renderer class and simply overriding or amending one of the emit methods:

// MyCustomRenderer.ts
import { CSharpRenderer } from "quicktype-core";

export class MyCustomRenderer extends CSharpRenderer {
    // Add your custom logic here, feel free to reference the source code for how existing methods work
    //
    // ex.
    protected superclassForType(t: Type): Sourcelike | undefined {
        // if the type is a class, it should extend `GameObject` when rendered in C#
        if (t instanceof ClassType) {
            return "GameObject";
        }
        return undefined;
    }
    // See: http://blog.quicktype.io/customizing-quicktype/ for more context
}

Creating a TargetLanguage

If you just want to change the rendering logic for an existing language, you can just extend an exported Language class (CSharpTargetLanguage in this example) and override the makeRenderer method:

// MyCustomLanguage.ts
import { CSharpTargetLanguage } from "quicktype-core";

import { MyCustomRenderer } from "./MyCustomRenderer";

export class MyCustomLanguage extends CSharpTargetLanguage {
    // `makeRenderer` instantiates the Renderer class for the TargetLanguage
    protected makeRenderer(
        renderContext: RenderContext,
        untypedOptionValues: Record<string, unknown>
    ): MyCustomRenderer {
        // use your new custom renderer class here
        return new MyCustomRenderer(this, renderContext, getOptionValues(cSharpOptions, untypedOptionValues));
    }
}

Using your custom Language

import { quicktype } from "quicktype-core";

import { MyCustomLanguage } from './MyCustomLanguage';

const lang = new MyCustomLanguage();

const lines = await quicktype({
	lang: lang, // use your new TargetLanguage in the `lang` field here
	...
});

console.log(lines);

Creating a new Language

If none of the existing quicktype Language classes suit your needs, you can creating your own TargetLanguge and Renderer classes from scratch. If this satisfies your use cases for a language we don't currently support, please consider opening a PR with your new language and we'd love to take a look.

If you run into any issues, you can open a GitHub issue and we'll help you take a look.

Creating a TargetLanguage from scratch

Instead of just extending an existing language, a new Language requires two additional steps:

  • Defining the language config
  • Adding any language-specific options
import { TargetLanguage, BooleanOption } from "quicktype-core";

// language config
const brandNewLanguageConfig = {
    displayName: "Scratch", // these can be the same
    names: ["scratch"], // these can be the same
    extension: "sb" // the file extension that this language commonly has
} as const;

// language options
const brandNewLanguageOptions = {
    allowFoo: new BooleanOption(
        "allow-foo", // option name
        "Allows Foo", // description
        true // default value
    )
    // The default available Option classes are: StringOption, BooleanOption, EnumOption
    // Please visit the source code for more examples and usage
};

class BrandNewLanguage extends TargetLanguage<typeof brandNewLanguageConfig> {
    public constructor() {
        super(brandNewLanguageConfig);
    }

    public getOptions(): typeof brandNewLanguageOptions {
        return brandNewLanguageOptions;
    }

    protected makeRenderer(
        renderContext: RenderContext,
        untypedOptionValues: Record<string, unknown>
    ): BrandNewRenderer {
        return new BrandNewRenderer(this, renderContext, getOptionValues(brandNewLanguageOptions, untypedOptionValues));
    }
}

Creating a Renderer from scratch

Creating a brand new Renderer class is very similar to extending an existing class:

export class BrandNewRenderer extends ConvenienceRenderer {
    public constructor(targetLanguage: TargetLanguage, renderContext: RenderContext) {
        super(targetLanguage, renderContext);
    }

    // Additional render methods go here
    // Please reference existing Renderer classes and open a GitHub issue if you need help
}

Links

Blog post with an older example: http://blog.quicktype.io/customizing-quicktype/