|
| 1 | +# Extending quicktype functionality with a Custom Renderer |
| 2 | + |
| 3 | +## quicktype Interface |
| 4 | + |
| 5 | +To customise your rendering output, you can extend existing quicktype classes and override existing methods to achieve the behaviour you want. |
| 6 | + |
| 7 | +This process requires 3 main steps: |
| 8 | + |
| 9 | +1. [Extending a `Renderer` Class](#creating-a-custom-renderer) |
| 10 | +2. [Wrapping your `Renderer` in a `TargetLanguage` Class](#creating-a-targetlanguage) |
| 11 | +3. [Using your new classes in the `quicktype` function](#using-your-custom-language) |
| 12 | +4. [Advanced Usage: Creating an entirely new Language](#creating-a-new-language) |
| 13 | + |
| 14 | +## Creating a custom `Renderer` |
| 15 | + |
| 16 | +Adding custom render logic for an existing language often involves extending a Renderer class and simply overriding or amending one of the `emit` methods: |
| 17 | + |
| 18 | +```ts |
| 19 | +// MyCustomRenderer.ts |
| 20 | +import { CSharpRenderer } from "quicktype-core"; |
| 21 | + |
| 22 | +export class MyCustomRenderer extends CSharpRenderer { |
| 23 | + // Add your custom logic here, feel free to reference the source code for how existing methods work |
| 24 | + // |
| 25 | + // ex. |
| 26 | + protected superclassForType(t: Type): Sourcelike | undefined { |
| 27 | + // if the type is a class, it should extend `GameObject` when rendered in C# |
| 28 | + if (t instanceof ClassType) { |
| 29 | + return "GameObject"; |
| 30 | + } |
| 31 | + return undefined; |
| 32 | + } |
| 33 | + // See: http://blog.quicktype.io/customizing-quicktype/ for more context |
| 34 | +} |
| 35 | +``` |
| 36 | + |
| 37 | +## Creating a `TargetLanguage` |
| 38 | + |
| 39 | +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: |
| 40 | + |
| 41 | +```ts |
| 42 | +// MyCustomLanguage.ts |
| 43 | +import { CSharpTargetLanguage } from "quicktype-core"; |
| 44 | + |
| 45 | +import { MyCustomRenderer } from "./MyCustomRenderer"; |
| 46 | + |
| 47 | +export class MyCustomLanguage extends CSharpTargetLanguage { |
| 48 | + // `makeRenderer` instantiates the Renderer class for the TargetLanguage |
| 49 | + protected makeRenderer( |
| 50 | + renderContext: RenderContext, |
| 51 | + untypedOptionValues: Record<string, unknown> |
| 52 | + ): MyCustomRenderer { |
| 53 | + // use your new custom renderer class here |
| 54 | + return new MyCustomRenderer(this, renderContext, getOptionValues(cSharpOptions, untypedOptionValues)); |
| 55 | + } |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +## Using your custom Language |
| 60 | + |
| 61 | +```ts |
| 62 | +import { quicktype } from "quicktype-core"; |
| 63 | + |
| 64 | +import { MyCustomLanguage } from './MyCustomLanguage'; |
| 65 | + |
| 66 | +const lang = new MyCustomLanguage(); |
| 67 | + |
| 68 | +const lines = await quicktype({ |
| 69 | +lang: lang, // use your new TargetLanguage in the `lang` field here |
| 70 | +... |
| 71 | +}); |
| 72 | + |
| 73 | +console.log(lines); |
| 74 | +``` |
| 75 | + |
| 76 | +## Creating a new Language |
| 77 | + |
| 78 | +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. |
| 79 | + |
| 80 | +If you run into any issues, you can open a GitHub issue and we'll help you take a look. |
| 81 | + |
| 82 | +### Creating a `TargetLanguage` from scratch |
| 83 | + |
| 84 | +Instead of just extending an existing language, a new Language requires two additional steps: |
| 85 | + |
| 86 | +- Defining the language config |
| 87 | +- Adding any language-specific options |
| 88 | + |
| 89 | +```ts |
| 90 | +import { TargetLanguage, BooleanOption } from "quicktype-core"; |
| 91 | + |
| 92 | +// language config |
| 93 | +const brandNewLanguageConfig = { |
| 94 | + displayName: "Scratch", // these can be the same |
| 95 | + names: ["scratch"], // these can be the same |
| 96 | + extension: "sb" // the file extension that this language commonly has |
| 97 | +} as const; |
| 98 | + |
| 99 | +// language options |
| 100 | +const brandNewLanguageOptions = { |
| 101 | + allowFoo: new BooleanOption( |
| 102 | + "allow-foo", // option name |
| 103 | + "Allows Foo", // description |
| 104 | + true // default value |
| 105 | + ) |
| 106 | + // The default available Option classes are: StringOption, BooleanOption, EnumOption |
| 107 | + // Please visit the source code for more examples and usage |
| 108 | +}; |
| 109 | + |
| 110 | +class BrandNewLanguage extends TargetLanguage<typeof brandNewLanguageConfig> { |
| 111 | + public constructor() { |
| 112 | + super(brandNewLanguageConfig); |
| 113 | + } |
| 114 | + |
| 115 | + public getOptions(): typeof brandNewLanguageOptions { |
| 116 | + return brandNewLanguageOptions; |
| 117 | + } |
| 118 | + |
| 119 | + protected makeRenderer( |
| 120 | + renderContext: RenderContext, |
| 121 | + untypedOptionValues: Record<string, unknown> |
| 122 | + ): BrandNewRenderer { |
| 123 | + return new BrandNewRenderer(this, renderContext, getOptionValues(brandNewLanguageOptions, untypedOptionValues)); |
| 124 | + } |
| 125 | +} |
| 126 | +``` |
| 127 | + |
| 128 | +### Creating a `Renderer` from scratch |
| 129 | + |
| 130 | +Creating a brand new `Renderer` class is very similar to extending an existing class: |
| 131 | + |
| 132 | +```ts |
| 133 | +export class BrandNewRenderer extends ConvenienceRenderer { |
| 134 | + public constructor(targetLanguage: TargetLanguage, renderContext: RenderContext) { |
| 135 | + super(targetLanguage, renderContext); |
| 136 | + } |
| 137 | + |
| 138 | + // Additional render methods go here |
| 139 | + // Please reference existing Renderer classes and open a GitHub issue if you need help |
| 140 | +} |
| 141 | +``` |
| 142 | + |
| 143 | +## Links |
| 144 | + |
| 145 | +Blog post with an older example: http://blog.quicktype.io/customizing-quicktype/ |
0 commit comments