grantila / typeconv

Convert between JSON Schema, TypeScript, GraphQL, Open API and SureType
MIT License
424 stars 8 forks source link
converter graphql jsonschema openapi3 suretype typescript

npm version downloads build status coverage status Node.JS version

typeconv is an extremely fast silver bullet type conversion utility.

It converts between any of its supported types, bidirectionally.

typeconv lets you convert between type systems which have core-types converters, such as JSON Schema, TypeScript, GraphQL, Open API and SureType. This package can be used as an API programatically or as an application (installed in node_modules/.bin or by using e.g. npx).

By taking advantage of the core-types (npm) toolbox for generic type handling, typeconv can convert and maintain source code location information, comments, descriptions etc. when converting between the different type systems. It is using the following converter packages:

These type systems don't share the same set of types and constraints. For example, JSON Schema has value constraints (like "a string must be longer than 5 characters") and GraphQL doesn't have null or key-value objects as a first-class type. Convertions will therefore produce the smallest common denominator of type information, but still be very useful. See core-types for more information on its supported types, and why not implement a new conversion package yourself!

TL;DR CLI

Convert files from TypeScript ("ts") to GraphQL ("gql"), put the generated files in the gql-schemas directory in the same directory structure as the source files:

$ typeconv -f ts -t gql -o gql-schemas 'types/**/*.ts'

This generates gql-schemas/*.graphql for each .ts file in types/ (and sub-directories).

Note that when using glob patterns, put them in quotes to not have the shell try to expand the pattern - typeconv will do it a lot better!

SureType

When converting from SureType, typeconv will extract all exported validators.

Versions

Since v2:

Contents

Conversion example

Converting the following JSON Schema:

```json { "definitions": { "User": { "type": "object", "title": "User type", "description": "This type holds the user information, such as name", "properties": { "name": { "type": "string", "title": "The real name" } }, "required": [ "name" ] }, "ChatLine": { "type": "object", "title": "A chat line", "properties": { "user": { "$ref": "#/definitions/User" }, "line": { "type": "string" } }, "required": [ "user", "line" ] } } } ```

... to TypeScript will produce:

```ts /*User type This type holds the user information, such as name*/ export interface User { /*The real name*/ name: string; } /*A chat line*/ export interface ChatLine { user: User; line: string; } ``` or if converted into TypeScript _declarations_ (for `.d.ts` files), `export interface` will be `export declare interface`.

... and to GraphQL will produce:

```graphql """ # User type This type holds the user information, such as name """ type User { "The real name" name: String! } "A chat line" type ChatLine { user: User! line: String! } ```

Conversions are bi-directional, so any of these type systems can convert to any other.

Usage

Command line

You can depend on typeconv, and if so, you'll have node_modules/.bin/typeconv installed. Or you run it with on-the-fly installation using npx, as npx typeconv args....

Use -f (or --from-type) to specify from which type system to convert, and -t (or --to-type) to specify which type system to convert to. Other options can be used to configure each configuration (both the from and the to) and these options are usually only available for a specific type system.

The types supported are gql (GraphQL), ts (TypeScript), jsc (JSON Schema), oapi (Open API) and st (SureType).

$ typeconv --help

``` Usage: typeconv [options] file ... Options: -h, --help Print (this) help screen --version Print the program version -v, --verbose Verbose informational output (default: false) --dry-run Prepare and perform conversion, but write no output (default: false) --(no-)hidden Include hidden files, i.e. files in .gitignore, files beginning with '.' and the '.git' directory (default: true) -f, --from-type Type system to convert from Values: ts TypeScript jsc JSON Schema gql GraphQL oapi Open API st SureType ct core-types -t, --to-type Type system to convert to Values: ts TypeScript jsc JSON Schema gql GraphQL oapi Open API st SureType ct core-types --(no-)shortcut Shortcut conversion if possible (bypassing core-types). This is possible between SureType, JSON Schema and Open API to preserve all features which would otherwise be erased. (default: true) -o, --output-directory

Output directory. Defaults to the same as the input files. -O, --output-extension Output filename extension to use. Defaults to 'ts'/'d.ts', 'json', 'yaml' and 'graphql'. Use '-' to not save a file, but output to stdout instead. --(no-)strip-annotations Removes all annotations (descriptions, comments, ...) (default: false) TypeScript --(no-)ts-declaration Output TypeScript declarations (default: false) --(no-)ts-disable-lint-header Output comments for disabling linting (default: true) --(no-)ts-descriptive-header Output the header comment (default: true) --(no-)ts-use-unknown Use 'unknown' type instead of 'any' (default: true) --ts-non-exported Strategy for non-exported types (default: include-if-referenced) Values: fail Fail conversion ignore Don't include non-exported types, even if referenced include Include non-exported types inline Don't include non-exported types, inline them if necessary. Will fail on cyclic types include-if-referenced Include non-exported types only if they are referenced from exported types --ts-namespaces Namespace strategy. (default: ignore) Values: ignore Ignore namespaces entirely (default). - When converting from TypeScript, types in namespaces aren't exported. - When converting to TypeScript, no attempt to reconstruct namespaces is performed. hoist When converting from TypeScript, hoist types inside namespaces to top-level, so that the types are included, but without their namespace. This can cause conflicts, in which case deeper declarations will be dropped in favor of more top- level declarations. In case of same-level (namespace depth) declarations with the same name, only one will be exported in a non-deterministic manner. dot When converting from TypeScript, join the namespaces and the exported type with a dot (.). When converting to TypeScript, try to reconstruct namespaces by splitting the name on dot (.). underscore When converting from TypeScript, join the namespaces and the exported type with an underscore (_). When converting to TypeScript, try to reconstruct namespaces by splitting the name on underscore (_). reconstruct-all When converting to TypeScript, try to reconstruct namespaces by splitting the name on both dot and underscore. GraphQL --gql-unsupported Method to use for unsupported types Values: ignore Ignore (skip) type warn Ignore type, but warn error Throw an error --gql-null-typename Custom type name to use for null Open API --oapi-format Output format for Open API (default: yaml) Values: json JSON yaml YAML ('yml' is also allowed) --oapi-title Open API title to use in output document. Defaults to the input filename. --oapi-version <version> Open API document version to use in output document. (default: 1) SureType --st-ref-method <method> SureType reference export method (default: provided) Values: no-refs Don't ref anything, inline all types provided Reference types that are explicitly exported ref-all Ref all provided types and those with names --st-missing-ref <method> What to do when detecting an unresolvable reference (default: warn) Values: ignore Ignore; skip type or cast to any warn Same as 'ignore', but warn error Fail conversion --(no-)st-inline-types Inline pretty typescript types aside validator code (default: true) --(no-)st-export-type Export the deduced types (or the pretty types, depending on --st-inline-types) (default: true) --(no-)st-export-schema Export validator schemas (default: false) --(no-)st-export-validator Export regular validators (default: true) --(no-)st-export-ensurer Export 'ensurer' validators (default: true) --(no-)st-export-type-guard Export type guards (is* validators) (default: true) --(no-)st-use-unknown Use 'unknown' type instead of 'any' (default: true) --(no-)st-forward-schema Forward the JSON Schema, and create an untyped validator schema with the raw JSON Schema under the hood (default: false) ``` </p> </details> <h2>As API</h2> <p>To convert from one type system to another, you create a <em>reader</em> for the type system to convert <strong>from</strong> and a <em>writer</em> for the type system to convert <strong>to</strong>. The readers and writers for the different type systems have their own set of options. Some have no options (like the JSON Schema reader), some require options (like the Open API writer).</p> <h3>makeConverter</h3> <p>Making a converter is done using <code>makeConverter(reader, writer, options?)</code>, which takes a reader and a writer, and optionally options:</p> <h4><strong>cwd</strong> (string)</h4> <p>The current working directory, only useful when working with files.</p> <h4><strong>simplify</strong> (boolean) (default true)</h4> <p>When simplify is true, the converter will let core-types <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types#simplify"><em>compress</em></a> the types after having converted from {reader} format to core-types. This is usually recommended, but may cause some annotations (comments) to be dropped.</p> <h4><strong>map</strong> (ConvertMapFunction)</h4> <p>Custom map function for transforming each type after it has been converted <em>from</em> the source type (and after it has been simplified), but before it's written to the target type system.</p> <p>Type: <code>(node: NamedType, index: number, array: ReadonlyArray<NamedType>) => NamedType</code> (<a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types#specification">NamedType ref</a>)</p> <p>If <code>filter</code> is used as well, this runs before <code>filter</code>.</p> <p>If <code>transform</code> is used as well, this runs before <code>transform</code>.</p> <h4><strong>filter</strong> (ConvertFilterFunction)</h4> <p>Custom filter function for filtering types after they have been converted <em>from</em> the source type.</p> <p>Type: <code>(node: NamedType, index: number, array: ReadonlyArray<NamedType>) => boolean</code> (<a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types#specification">NamedType ref</a>)</p> <p>If <code>map</code> is used as well, this runs after <code>map</code>.</p> <p>If <code>transform</code> is used as well, this runs before <code>transform</code>.</p> <h4><strong>transform</strong> (ConvertTransformFunction)</h4> <p>Custom filter function for filtering types after they have been converted <em>from</em> the source type.</p> <p>Type: <code>( doc: NodeDocument ) => NodeDocument</code> (<a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types#specification">NodeDocument ref</a>)</p> <p>If <code>map</code> is used as well, this runs after <code>map</code>.</p> <p>If <code>filter</code> is used as well, this runs after <code>filter</code>.</p> <h4><strong>shortcut</strong> (boolean) (default true)</h4> <p>Shortcut reader and writer if possible (bypassing core-types).</p> <h3>Basic example conversion</h3> <pre><code class="language-ts">import { getTypeScriptReader, getOpenApiWriter, makeConverter, } from 'typeconv' const reader = getTypeScriptReader( ); const writer = getOpenApiWriter( { format: 'yaml', title: 'My API', version: 'v1' } ); const { convert } = makeConverter( reader, writer ); const { data } = await convert( { data: "export type Foo = string | number;" } ); data; // This is the Open API yaml as a string</code></pre> <h3>JSON Schema</h3> <p>There are two exported functions for JSON Schema:</p> <pre><code class="language-ts">import { getJsonSchemaReader, getJsonSchemaWriter } from 'typeconv' const reader = getJsonSchemaReader( ); const writer = getJsonSchemaWriter( );</code></pre> <p>They don't have any options.</p> <p>typeconv expects the JSON Schema to contain <strong>definitions</strong>, i.e. to be in the form:</p> <details style="padding-left: 32px;border-left: 4px solid gray;"> <summary>JSON Schema</summary> <p> ```json { "definitions": { "User": { "type": "object", "properties": { "name": { "type": "string" } }, "required": [ "name" ] }, "ChatLine": { "type": "object", "properties": { "user": { "$ref": "#/definitions/User" }, "line": { "type": "string" } }, "required": [ "user", "line" ] } } } ``` </p> </details> <p>typeconv doesn't support external references (to other files). If you have that, you need to use a reference parser and merge it into one inline-referenced file before using typeconv.</p> <h3>Open API</h3> <p>Converting to or from Open API can be done with both JSON and YAML. The default is JSON.</p> <p>When reading, if the filename ends with <code>.yml</code> or <code>.yaml</code>, typeconv will interpret the input as YAML.</p> <p>Writing however, is decided in the writer factory and provided to <code>getOpenApiWriter</code>.</p> <pre><code class="language-ts">import { getOpenApiReader, getOpenApiWriter } from 'typeconv' const reader = getOpenApiReader( ); const writer = getOpenApiWriter( { format: 'yaml', title: 'My API', version: 'v1', schemaVersion: '3.0.0', } );</code></pre> <p>The options to <code>getOpenApiWriter</code> is:</p> <pre><code class="language-ts">interface { format?: string; title: string; version: string; schemaVersion?: string; }</code></pre> <h3>TypeScript</h3> <p>TypeScript conversion is done using:</p> <pre><code class="language-ts">import { getTypeScriptReader, getTypeScriptWriter } from 'typeconv' const reader = getTypeScriptReader( ); const writer = getTypeScriptWriter( );</code></pre> <p>Both these take an optional argument.</p> <p>The <code>getTypeScriptReader</code> takes an optional <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types-ts#typescript-to-core-types"><code>FromTsOptions</code></a> object from <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types-ts"><code>core-types-ts</code></a>, although <code>warn</code> isn't necessary since it's set by typeconv internally.</p> <p>The <code>getTypeScriptWriter</code> takes an optional <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types-ts#core-types-to-typescript"><code>ToTsOptions</code></a> object from <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types-ts"><code>core-types-ts</code></a>, although <code>warn</code>, <code>filename</code>, <code>sourceFilename</code>, <code>userPackage</code> and <code>userPackageUrl</code> aren't necessary since they're set by typeconv internally.</p> <h3>GraphQL</h3> <p>GraphQL conversion is done using;</p> <pre><code class="language-ts">import { getGraphQLReader, getGraphQLWriter } from 'typeconv' const reader = getGraphQLReader( ); const writer = getGraphQLWriter( );</code></pre> <p>Both these take an optional argument.</p> <p>The <code>getGraphQLReader</code> takes an optional <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types-graphql#graphql-to-core-types"><code>GraphqlToCoreTypesOptions</code></a> object from <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types-graphql"><code>core-types-graphql</code></a>, although <code>warn</code> isn't necessary since it's set by typeconv internally.</p> <p>The <code>getGraphQLWriter</code> takes an optional <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types-graphql#core-types-to-graphql"><code>CoreTypesToGraphqlOptions</code></a> object from <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types-graphql"><code>core-types-graphql</code></a>, although <code>warn</code>, <code>filename</code>, <code>sourceFilename</code>, <code>userPackage</code> and <code>userPackageUrl</code> aren't necessary since they're set by typeconv internally.</p> <h3>SureType</h3> <p>SureType conversion is done using;</p> <pre><code class="language-ts">import { getSureTypeReader, getSureTypeWriter } from 'typeconv' const reader = getSureTypeReader( ); const writer = getSureTypeWriter( );</code></pre> <p>Both these take an optional argument from the <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types-suretype"><code>core-types-suretype</code></a> package.</p> <p>The <code>getSureTypeReader</code> takes an optional <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types-suretype#suretype-to-core-types"><code>SuretypeToJsonSchemaOptions</code></a>.</p> <p>The <code>getSureTypeWriter</code> takes an optional <a rel="noreferrer nofollow" target="_blank" href="https://github.com/grantila/core-types-suretype#core-types-to-suretype"><code>JsonSchemaToSuretypeOptions</code></a>.</p></div> </div> <div class="footer"> <ul class="body"> <li>© <script> document.write(new Date().getFullYear()) </script> Githubissues.</li> <li>Githubissues is a development platform for aggregating issues.</li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script> <script src="/githubissues/assets/js.js"></script> <script src="/githubissues/assets/markdown.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/highlight.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/languages/go.min.js"></script> <script> hljs.highlightAll(); </script> </body> </html>