From 417626735404f1b32a92b8e6a67ec77ea7c5f274 Mon Sep 17 00:00:00 2001 From: Rune Finstad Halvorsen Date: Fri, 9 Apr 2021 22:03:16 +0200 Subject: [PATCH 1/6] experimental support for emitting static types --- src/__tests__/main.test.ts | 37 +++++++++++++++++++++++++++++++++++++ src/main.ts | 19 +++++++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/__tests__/main.test.ts b/src/__tests__/main.test.ts index 9086a4e..f0ebf66 100644 --- a/src/__tests__/main.test.ts +++ b/src/__tests__/main.test.ts @@ -312,6 +312,43 @@ describe('runtype generation', () => { expect(source).not.toMatch(/;/); }); + it('output types', () => { + const source = generateRuntypes( + [ + { + name: 'thing', + type: { + kind: 'record', + fields: [{ name: 'tag', type: { kind: 'string' } }], + }, + }, + { + name: 'things', + type: { kind: 'array', type: { kind: 'named', name: 'thing' } }, + }, + { name: 'name', type: { kind: 'string' } }, + ], + { includeTypes: true }, + ); + + expect(source).toMatchInlineSnapshot(` + "import * as rt from \\"runtypes\\"; + + const thing = rt.Record({ tag: rt.String }); + + type thing = rt.Static; + + const things = rt.Array(thing); + + type things = rt.Static; + + const name = rt.String; + + type name = rt.Static; + " + `); + }); + it.todo('Array'); it.todo('Boolean'); it.todo('Brand'); diff --git a/src/main.ts b/src/main.ts index 8571bb6..c890cc6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,5 @@ import { Options as PrettierOptions, format } from 'prettier'; +import * as rt from 'runtypes'; import { AnyType, ArrayType, @@ -54,11 +55,13 @@ export interface GenerateOptions { format?: boolean; formatOptions?: PrettierOptions; includeImport?: boolean; + includeTypes?: boolean; } const defaultOptions: GenerateOptions = { format: true, includeImport: true, + includeTypes: false, }; export function generateRuntypes( @@ -73,7 +76,7 @@ export function generateRuntypes( allOptions.includeImport, 'import * as rt from "runtypes";\n\n', ); - roots.forEach((root) => writeRootType(writer, root)); + roots.forEach((root) => writeRootType(allOptions, writer, root)); const source = writer.getSource(); return allOptions.format @@ -81,11 +84,23 @@ export function generateRuntypes( : source.trim(); } -function writeRootType(w: CodeWriter, node: RootType) { +function writeRootType( + options: GenerateOptions, + w: CodeWriter, + node: RootType, +) { + const { includeTypes } = options; w.conditionalWrite(Boolean(node.export), 'export '); w.write(`const ${node.name}=`); writeAnyType(w, node.type); w.write(';\n\n'); + + w.conditionalWrite(Boolean(node.export) && includeTypes, 'export '); + w.conditionalWrite( + includeTypes, + `type ${node.name}=rt.Static;`, + ); + w.write('\n\n'); } // fixme: use mapped type so `node` is typed more narrowly maybe From 6edaf0b50fe6fe3b44df8a24d7226ed67a21c527 Mon Sep 17 00:00:00 2001 From: Rune Finstad Halvorsen Date: Fri, 9 Apr 2021 22:12:26 +0200 Subject: [PATCH 2/6] fix import --- src/main.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index c890cc6..a79cc98 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,4 @@ import { Options as PrettierOptions, format } from 'prettier'; -import * as rt from 'runtypes'; import { AnyType, ArrayType, From e4ad691d019c99b87c9ea3ea269790e8c26abce4 Mon Sep 17 00:00:00 2001 From: Rune Finstad Halvorsen Date: Fri, 9 Apr 2021 22:37:08 +0200 Subject: [PATCH 3/6] Make generating types the default --- src/__tests__/main.test.ts | 20 +++++++++++++++++++- src/main.ts | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/__tests__/main.test.ts b/src/__tests__/main.test.ts index f0ebf66..576ceb2 100644 --- a/src/__tests__/main.test.ts +++ b/src/__tests__/main.test.ts @@ -97,6 +97,8 @@ describe('runtype generation', () => { const personRt = rt.Record({ name: rt.String, age: rt.Number }).asReadonly(); + type personRt = rt.Static; + export const smokeTest = rt.Record({ someBoolean: rt.Boolean, someNever: rt.Never, @@ -127,6 +129,8 @@ describe('runtype generation', () => { ), }), }); + + export type smokeTest = rt.Static; " `); }); @@ -164,6 +168,8 @@ describe('runtype generation', () => { "import * as rt from \\"runtypes\\"; const test = rt.Record({ name: rt.String }); + + type test = rt.Static; " `); }); @@ -180,6 +186,8 @@ describe('runtype generation', () => { "import * as rt from \\"runtypes\\"; const test = rt.Record({ name: rt.String }).asReadonly(); + + type test = rt.Static; " `); }); @@ -196,6 +204,8 @@ describe('runtype generation', () => { "import * as rt from \\"runtypes\\"; const test = rt.Record({ name: rt.String }).asPartial(); + + type test = rt.Static; " `); }); @@ -219,6 +229,8 @@ describe('runtype generation', () => { "import * as rt from \\"runtypes\\"; const test = rt.Record({ name: rt.String }).asPartial().asReadonly(); + + type test = rt.Static; " `); }); @@ -261,6 +273,8 @@ describe('runtype generation', () => { rt.Record({ field_3: rt.String }).asReadonly(), rt.Record({ field_4: rt.String }).asPartial().asReadonly() ); + + type test = rt.Static; " `); }); @@ -285,6 +299,8 @@ describe('runtype generation', () => { const person = rt .Record({ id: rt.String, name: rt.String, age: rt.String }) .asReadonly(); + + type person = rt.Static; " `); @@ -292,7 +308,9 @@ describe('runtype generation', () => { expect(sourceUnformatted).toMatchInlineSnapshot(` "import * as rt from \\"runtypes\\"; - const person=rt.Record({id:rt.String,name:rt.String,age:rt.String,}).asReadonly();" + const person=rt.Record({id:rt.String,name:rt.String,age:rt.String,}).asReadonly(); + + type person=rt.Static;" `); }); diff --git a/src/main.ts b/src/main.ts index a79cc98..ca58787 100644 --- a/src/main.ts +++ b/src/main.ts @@ -60,7 +60,7 @@ export interface GenerateOptions { const defaultOptions: GenerateOptions = { format: true, includeImport: true, - includeTypes: false, + includeTypes: true, }; export function generateRuntypes( From 138123655adc7212a1c84584d41f037a8352b70a Mon Sep 17 00:00:00 2001 From: Rune Finstad Halvorsen Date: Fri, 9 Apr 2021 22:47:03 +0200 Subject: [PATCH 4/6] Formatting of names --- src/__tests__/main.test.ts | 24 ++++++++++++------------ src/main.ts | 14 +++++++++++--- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/__tests__/main.test.ts b/src/__tests__/main.test.ts index 576ceb2..c2ec528 100644 --- a/src/__tests__/main.test.ts +++ b/src/__tests__/main.test.ts @@ -97,7 +97,7 @@ describe('runtype generation', () => { const personRt = rt.Record({ name: rt.String, age: rt.Number }).asReadonly(); - type personRt = rt.Static; + type PersonRt = rt.Static; export const smokeTest = rt.Record({ someBoolean: rt.Boolean, @@ -130,7 +130,7 @@ describe('runtype generation', () => { }), }); - export type smokeTest = rt.Static; + export type SmokeTest = rt.Static; " `); }); @@ -169,7 +169,7 @@ describe('runtype generation', () => { const test = rt.Record({ name: rt.String }); - type test = rt.Static; + type Test = rt.Static; " `); }); @@ -187,7 +187,7 @@ describe('runtype generation', () => { const test = rt.Record({ name: rt.String }).asReadonly(); - type test = rt.Static; + type Test = rt.Static; " `); }); @@ -205,7 +205,7 @@ describe('runtype generation', () => { const test = rt.Record({ name: rt.String }).asPartial(); - type test = rt.Static; + type Test = rt.Static; " `); }); @@ -230,7 +230,7 @@ describe('runtype generation', () => { const test = rt.Record({ name: rt.String }).asPartial().asReadonly(); - type test = rt.Static; + type Test = rt.Static; " `); }); @@ -274,7 +274,7 @@ describe('runtype generation', () => { rt.Record({ field_4: rt.String }).asPartial().asReadonly() ); - type test = rt.Static; + type Test = rt.Static; " `); }); @@ -300,7 +300,7 @@ describe('runtype generation', () => { .Record({ id: rt.String, name: rt.String, age: rt.String }) .asReadonly(); - type person = rt.Static; + type Person = rt.Static; " `); @@ -310,7 +310,7 @@ describe('runtype generation', () => { const person=rt.Record({id:rt.String,name:rt.String,age:rt.String,}).asReadonly(); - type person=rt.Static;" + type Person=rt.Static;" `); }); @@ -354,15 +354,15 @@ describe('runtype generation', () => { const thing = rt.Record({ tag: rt.String }); - type thing = rt.Static; + type Thing = rt.Static; const things = rt.Array(thing); - type things = rt.Static; + type Things = rt.Static; const name = rt.String; - type name = rt.Static; + type Name = rt.Static; " `); }); diff --git a/src/main.ts b/src/main.ts index ca58787..4af0f3f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -50,17 +50,23 @@ function makeWriter(): CodeWriter { }; } +type NameFunction = (originalName: string) => string; + export interface GenerateOptions { format?: boolean; formatOptions?: PrettierOptions; includeImport?: boolean; includeTypes?: boolean; + getRuntypeName?: NameFunction; + getTypeName?: NameFunction; } const defaultOptions: GenerateOptions = { format: true, includeImport: true, includeTypes: true, + getRuntypeName: (e) => e, + getTypeName: (e) => e[0].toUpperCase() + e.slice(1), }; export function generateRuntypes( @@ -88,16 +94,18 @@ function writeRootType( w: CodeWriter, node: RootType, ) { - const { includeTypes } = options; + const { includeTypes, getRuntypeName, getTypeName } = options; + const runtypeName = getRuntypeName(node.name); + const typeName = getTypeName(node.name); w.conditionalWrite(Boolean(node.export), 'export '); - w.write(`const ${node.name}=`); + w.write(`const ${runtypeName}=`); writeAnyType(w, node.type); w.write(';\n\n'); w.conditionalWrite(Boolean(node.export) && includeTypes, 'export '); w.conditionalWrite( includeTypes, - `type ${node.name}=rt.Static;`, + `type ${typeName}=rt.Static;`, ); w.write('\n\n'); } From f2f83da81aefc25a3504a6650098745ac0fe3734 Mon Sep 17 00:00:00 2001 From: Rune Finstad Halvorsen Date: Fri, 9 Apr 2021 22:55:12 +0200 Subject: [PATCH 5/6] Test for custom name formatters --- src/__tests__/main.test.ts | 87 +++++++++++++++++++++++++++----------- src/main.ts | 14 +++--- 2 files changed, 69 insertions(+), 32 deletions(-) diff --git a/src/__tests__/main.test.ts b/src/__tests__/main.test.ts index c2ec528..6c32968 100644 --- a/src/__tests__/main.test.ts +++ b/src/__tests__/main.test.ts @@ -330,41 +330,78 @@ describe('runtype generation', () => { expect(source).not.toMatch(/;/); }); - it('output types', () => { - const source = generateRuntypes( - [ - { - name: 'thing', - type: { - kind: 'record', - fields: [{ name: 'tag', type: { kind: 'string' } }], + describe('output types', () => { + it('can omit the types', () => { + const source = generateRuntypes( + [ + { + name: 'thing', + type: { + kind: 'record', + fields: [{ name: 'tag', type: { kind: 'string' } }], + }, }, - }, + { + name: 'things', + type: { kind: 'array', type: { kind: 'named', name: 'thing' } }, + }, + { name: 'name', type: { kind: 'string' } }, + ], + { includeTypes: false }, + ); + + expect(source).toMatchInlineSnapshot(` + "import * as rt from \\"runtypes\\"; + + const thing = rt.Record({ tag: rt.String }); + + const things = rt.Array(thing); + + const name = rt.String; + " + `); + }); + + it('can pass in name formatter', () => { + const source = generateRuntypes( + [ + { + name: 'thing', + type: { + kind: 'record', + fields: [{ name: 'tag', type: { kind: 'string' } }], + }, + }, + { + name: 'things', + export: true, + type: { kind: 'array', type: { kind: 'named', name: 'thing' } }, + }, + { name: 'name', type: { kind: 'string' } }, + ], { - name: 'things', - type: { kind: 'array', type: { kind: 'named', name: 'thing' } }, + formatRuntypeName: (e) => `${e}Runtype`, + formatTypeName: (e) => `${e}Type`, }, - { name: 'name', type: { kind: 'string' } }, - ], - { includeTypes: true }, - ); + ); - expect(source).toMatchInlineSnapshot(` - "import * as rt from \\"runtypes\\"; + expect(source).toMatchInlineSnapshot(` + "import * as rt from \\"runtypes\\"; - const thing = rt.Record({ tag: rt.String }); + const thingRuntype = rt.Record({ tag: rt.String }); - type Thing = rt.Static; + type thingType = rt.Static; - const things = rt.Array(thing); + export const thingsRuntype = rt.Array(thing); - type Things = rt.Static; + export type thingsType = rt.Static; - const name = rt.String; + const nameRuntype = rt.String; - type Name = rt.Static; - " - `); + type nameType = rt.Static; + " + `); + }); }); it.todo('Array'); diff --git a/src/main.ts b/src/main.ts index 4af0f3f..39ca97a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -57,16 +57,16 @@ export interface GenerateOptions { formatOptions?: PrettierOptions; includeImport?: boolean; includeTypes?: boolean; - getRuntypeName?: NameFunction; - getTypeName?: NameFunction; + formatRuntypeName?: NameFunction; + formatTypeName?: NameFunction; } const defaultOptions: GenerateOptions = { format: true, includeImport: true, includeTypes: true, - getRuntypeName: (e) => e, - getTypeName: (e) => e[0].toUpperCase() + e.slice(1), + formatRuntypeName: (e) => e, + formatTypeName: (e) => e[0].toUpperCase() + e.slice(1), }; export function generateRuntypes( @@ -94,9 +94,9 @@ function writeRootType( w: CodeWriter, node: RootType, ) { - const { includeTypes, getRuntypeName, getTypeName } = options; - const runtypeName = getRuntypeName(node.name); - const typeName = getTypeName(node.name); + const { formatRuntypeName, formatTypeName, includeTypes } = options; + const runtypeName = formatRuntypeName(node.name); + const typeName = formatTypeName(node.name); w.conditionalWrite(Boolean(node.export), 'export '); w.write(`const ${runtypeName}=`); writeAnyType(w, node.type); From ce0e79ae3e16ce74bb9d1f9c42e6640b3e39ed59 Mon Sep 17 00:00:00 2001 From: Rune Date: Sat, 10 Apr 2021 14:32:18 +0200 Subject: [PATCH 6/6] Update src/main.ts Co-authored-by: Mikalai Ziuz --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 39ca97a..9b6722d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -65,7 +65,7 @@ const defaultOptions: GenerateOptions = { format: true, includeImport: true, includeTypes: true, - formatRuntypeName: (e) => e, + formatRuntypeName: (e) => e[0].toLowerCase() + e.slice(1), formatTypeName: (e) => e[0].toUpperCase() + e.slice(1), };