diff --git a/flow-typed/npm/prettier_vx.x.x.js b/flow-typed/npm/prettier_vx.x.x.js deleted file mode 100644 index 5d5dba2..0000000 --- a/flow-typed/npm/prettier_vx.x.x.js +++ /dev/null @@ -1,7 +0,0 @@ -// @flow - -declare module 'prettier' { - declare module.exports: { - format(string, config?: mixed): string, - }; -} diff --git a/package.json b/package.json index bd61744..e0ecf12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "thrift2flow", - "version": "0.11.5", + "version": "0.12.0", "description": "Convert Thrift definitions to Flowtype declarations", "homepage": "https://github.com/uber-node/thrift2flow", "bugs": { @@ -43,7 +43,6 @@ "babel-polyfill": "^6.23.0", "common-path-prefix": "^1.0.0", "mkdirp": "^0.5.1", - "prettier": "^1.14.2", "source-map-support": "^0.4.15", "thriftrw": "^3.11.0", "uuid": "^3.3.2", @@ -68,6 +67,7 @@ "flow-bin": "^0.94.0", "fs-extra": "^4.0.3", "jest": "^24.1.0", + "prettier": "^1.14.2", "tmp": "^0.0.33" } } diff --git a/src/__tests__/__snapshots__/enums.test.js.snap b/src/__tests__/__snapshots__/enums.test.js.snap index 2831dc2..38fb345 100644 --- a/src/__tests__/__snapshots__/enums.test.js.snap +++ b/src/__tests__/__snapshots__/enums.test.js.snap @@ -4,11 +4,11 @@ exports[`enum to JS 1`] = ` "// @flow export const MyEnum: $ReadOnly<{| - OK: \\"OK\\", - ERROR: \\"ERROR\\" + 'OK': 'OK', + 'ERROR': 'ERROR', |}> = Object.freeze({ - OK: \\"OK\\", - ERROR: \\"ERROR\\" + 'OK': 'OK', + 'ERROR': 'ERROR', }); " `; @@ -17,19 +17,19 @@ exports[`enums work with typedefs 1`] = ` "// @flow export const MyEnum: $ReadOnly<{| - OK: \\"OK\\", - ERROR: \\"ERROR\\" + 'OK': 'OK', + 'ERROR': 'ERROR', |}> = Object.freeze({ - OK: \\"OK\\", - ERROR: \\"ERROR\\" + 'OK': 'OK', + 'ERROR': 'ERROR', }); export const MyOtherEnum: $ReadOnly<{| - OK: \\"OK\\", - ERROR: \\"ERROR\\" + 'OK': 'OK', + 'ERROR': 'ERROR', |}> = Object.freeze({ - OK: \\"OK\\", - ERROR: \\"ERROR\\" + 'OK': 'OK', + 'ERROR': 'ERROR', }); " `; diff --git a/src/__tests__/buffer.test.js b/src/__tests__/buffer.test.js index f684eb3..ace6f87 100644 --- a/src/__tests__/buffer.test.js +++ b/src/__tests__/buffer.test.js @@ -37,11 +37,11 @@ test('See how thriftrw decodes js.type i64', () => { expect(thrift.MY_BUFF).toEqual(10); const converter = new ThriftFileConverter(fixturePath, false); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow + "// @flow -export const MY_BUFF: 10 = 10; -" -`); + export const MY_BUFF: 10 = 10; + " + `); }); test('Try using the toBufferResult fromBufferResult when parsing a struct with an i64 value', () => { @@ -56,11 +56,13 @@ test('Try using the toBufferResult fromBufferResult when parsing a struct with a expect(Buffer.isBuffer(structAgain.value.myProp)).toBeTruthy(); const converter = new ThriftFileConverter(fixturePath, false); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow + "// @flow -export type MyStruct = {| myProp: number | Buffer |}; -" -`); + export type MyStruct = {| + myProp: (number | Buffer), + |}; + " + `); }); test('Ensure flow uses number not buffer for i64', () => { @@ -72,11 +74,11 @@ test('Ensure flow uses number not buffer for i64', () => { expect(thrift.NULL_ID).toEqual(0); const converter = new ThriftFileConverter(fixturePath, false); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow + "// @flow -export type MY_ID = number | Buffer; + export type MY_ID = (number | Buffer); -export const NULL_ID: MY_ID = 0; -" -`); + export const NULL_ID: MY_ID = 0; + " + `); }); diff --git a/src/__tests__/consts/const-lists.test.js b/src/__tests__/consts/const-lists.test.js index 6d732e2..83f5e67 100644 --- a/src/__tests__/consts/const-lists.test.js +++ b/src/__tests__/consts/const-lists.test.js @@ -42,29 +42,29 @@ test('const map values are numbers', () => { ); const jsContent = converter.generateFlowFile(); expect(jsContent).toMatchInlineSnapshot(` -"// @flow + "// @flow -export const Direction: $ReadOnly<{| - LEFT: \\"LEFT\\", - RIGHT: \\"RIGHT\\" -|}> = Object.freeze({ - LEFT: \\"LEFT\\", - RIGHT: \\"RIGHT\\" -}); + export const Direction: $ReadOnly<{| + 'LEFT': 'LEFT', + 'RIGHT': 'RIGHT', + |}> = Object.freeze({ + 'LEFT': 'LEFT', + 'RIGHT': 'RIGHT', + }); -export const DIRECTIONS: $Values[] = [ - Direction.LEFT, - Direction.RIGHT, - Direction.LEFT, - Direction.RIGHT -]; + export const DIRECTIONS: $Values[] = [ + Direction.LEFT, + Direction.RIGHT, + Direction.LEFT, + Direction.RIGHT, + ]; -export const DIRECTIONS_LIST: $Values[] = [ - Direction.LEFT, - Direction.RIGHT, - Direction.LEFT, - Direction.RIGHT -]; -" -`); + export const DIRECTIONS_LIST: $Values[] = [ + Direction.LEFT, + Direction.RIGHT, + Direction.LEFT, + Direction.RIGHT, + ]; + " + `); }); diff --git a/src/__tests__/consts/const-map.test.js b/src/__tests__/consts/const-map.test.js index dc537cb..280b71b 100644 --- a/src/__tests__/consts/const-map.test.js +++ b/src/__tests__/consts/const-map.test.js @@ -38,16 +38,19 @@ test('convert const map witth enums', () => { expect(thrift.USER_TYPES.user).toEqual(true); const jsContent = converter.generateFlowFile(); expect(jsContent).toMatchInlineSnapshot(` -"// @flow + "// @flow -export const ADMIN_FOOO: \\"admin\\" = \\"admin\\"; + export const ADMIN_FOOO: 'admin' = 'admin'; -export const USER_BAAAR: \\"user\\" = \\"user\\"; + export const USER_BAAAR: 'user' = 'user'; -export const USER_TYPES: $ReadOnly<{| admin: boolean, user: boolean |}> = { - [ADMIN_FOOO]: true, - [USER_BAAAR]: true -}; -" -`); + export const USER_TYPES: $ReadOnly<{| + 'admin': boolean, + 'user': boolean, + |}> = { + [ADMIN_FOOO]: true, + [USER_BAAAR]: true, + }; + " + `); }); diff --git a/src/__tests__/consts/consts.test.js b/src/__tests__/consts/consts.test.js index 42b812a..4a00e22 100644 --- a/src/__tests__/consts/consts.test.js +++ b/src/__tests__/consts/consts.test.js @@ -52,12 +52,11 @@ test('const string literals', () => { ); const jsContent = converter.generateFlowFile(); expect(jsContent).toMatchInlineSnapshot(` -"// @flow + "// @flow -export const A_STRING_LITERAL: \\"my-string-literal-value\\" = - \\"my-string-literal-value\\"; -" -`); + export const A_STRING_LITERAL: 'my-string-literal-value' = 'my-string-literal-value'; + " + `); }); test('const map values are numbers', () => { @@ -67,49 +66,69 @@ test('const map values are numbers', () => { ); const jsContent = converter.generateFlowFile(); expect(jsContent).toMatchInlineSnapshot(` -"// @flow - -export const ShieldType: $ReadOnly<{| - O: \\"O\\", - U: \\"U\\" -|}> = Object.freeze({ - O: \\"O\\", - U: \\"U\\" -}); - -export const o: \\"ooooooo\\" = \\"ooooooo\\"; - -export const PRIORITIES: $ReadOnly<{| O: number, U: number |}> = { - [ShieldType.O]: 2, - [ShieldType.U]: 10 -}; - -export const LABELS: $ReadOnly<{| O: string, U: string |}> = { - [ShieldType.O]: o, - [ShieldType.U]: \\"uuuuuuu\\" -}; - -export const THINGS: $ReadOnly<{| O: string[], U: string[] |}> = { - [ShieldType.O]: [o, \\"abcd\\"], - [ShieldType.U]: [\\"uuuuuuu\\"] -}; - -export const ITEMS: $Values[] = [ShieldType.O, ShieldType.U]; - -export const MAP_CONST_LIST: $ReadOnly<{| - O: $Values[], - U: $Values[] -|}> = { - [ShieldType.O]: ITEMS, - [ShieldType.U]: [] -}; - -export const NUMS: $ReadOnly<{| \\"0\\": string, \\"1\\": string |}> = { - \\"0\\": \\"aaa\\", - \\"1\\": \\"bbb\\" -}; -" -`); + "// @flow + + export const ShieldType: $ReadOnly<{| + 'O': 'O', + 'U': 'U', + |}> = Object.freeze({ + 'O': 'O', + 'U': 'U', + }); + + export const o: 'ooooooo' = 'ooooooo'; + + export const PRIORITIES: $ReadOnly<{| + 'O': number, + 'U': number, + |}> = { + [ShieldType.O]: 2, + [ShieldType.U]: 10, + }; + + export const LABELS: $ReadOnly<{| + 'O': string, + 'U': string, + |}> = { + [ShieldType.O]: o, + [ShieldType.U]: 'uuuuuuu', + }; + + export const THINGS: $ReadOnly<{| + 'O': string[], + 'U': string[], + |}> = { + [ShieldType.O]: [ + o, + \\"abcd\\", + ], + [ShieldType.U]: [ + \\"uuuuuuu\\", + ], + }; + + export const ITEMS: $Values[] = [ + ShieldType.O, + ShieldType.U, + ]; + + export const MAP_CONST_LIST: $ReadOnly<{| + 'O': $Values[], + 'U': $Values[], + |}> = { + [ShieldType.O]: ITEMS, + [ShieldType.U]: [], + }; + + export const NUMS: $ReadOnly<{| + '0': string, + '1': string, + |}> = { + '0': 'aaa', + '1': 'bbb', + }; + " + `); }); test('constant enum values', () => { @@ -119,23 +138,23 @@ test('constant enum values', () => { ); const jsContent = converter.generateFlowFile(); expect(jsContent).toMatchInlineSnapshot(` -"// @flow - -export const PlaceType: $ReadOnly<{| - A: \\"A\\", - B: \\"B\\" -|}> = Object.freeze({ - A: \\"A\\", - B: \\"B\\" -}); - -export const UUID_TO_PLACE_TYPE: $ReadOnly<{| - \\"123\\": $Values, - \\"456\\": $Values -|}> = { - \\"123\\": PlaceType.A, - \\"456\\": PlaceType.B -}; -" -`); + "// @flow + + export const PlaceType: $ReadOnly<{| + 'A': 'A', + 'B': 'B', + |}> = Object.freeze({ + 'A': 'A', + 'B': 'B', + }); + + export const UUID_TO_PLACE_TYPE: $ReadOnly<{| + '123': $Values, + '456': $Values, + |}> = { + '123': PlaceType.A, + '456': PlaceType.B, + }; + " + `); }); diff --git a/src/__tests__/consts/lists.test.js b/src/__tests__/consts/lists.test.js index 49bbe13..cc103ca 100644 --- a/src/__tests__/consts/lists.test.js +++ b/src/__tests__/consts/lists.test.js @@ -14,29 +14,41 @@ test('const lists are transformed correctly', () => { const converter = new ThriftFileConverter(fixturePath, false); const jsContent = converter.generateFlowFile(); expect(jsContent).toMatchInlineSnapshot(` -"// @flow + "// @flow -export const AAA: \\"AAA\\" = \\"AAA\\"; + export const AAA: 'AAA' = 'AAA'; -export const BBB: \\"BBB\\" = \\"BBB\\"; + export const BBB: 'BBB' = 'BBB'; -export const CCC: \\"CCC\\" = \\"CCC\\"; + export const CCC: 'CCC' = 'CCC'; -export const DDD: \\"DDD\\" = \\"DDD\\"; + export const DDD: 'DDD' = 'DDD'; -export const EEE: \\"EEE\\" = \\"EEE\\"; + export const EEE: 'EEE' = 'EEE'; -export const FFF: \\"FFF\\" = \\"FFF\\"; + export const FFF: 'FFF' = 'FFF'; -export const BBB_LIST: string[] = [BBB]; + export const BBB_LIST: string[] = [ + BBB, + ]; -export const DDDEEEFFF: string[] = [DDD, EEE, FFF]; + export const DDDEEEFFF: string[] = [ + DDD, + EEE, + FFF, + ]; -export const DDDEEEFFF_ALIAS = DDDEEEFFF; + export const DDDEEEFFF_ALIAS = DDDEEEFFF -export const CCCAAA: string[] = [CCC, AAA]; + export const CCCAAA: string[] = [ + CCC, + AAA, + ]; -export const CUSTOM_AND_DL_MODEL_TYPES: string[][] = [DDDEEEFFF_ALIAS, CCCAAA]; -" -`); + export const CUSTOM_AND_DL_MODEL_TYPES: string[][] = [ + DDDEEEFFF_ALIAS, + CCCAAA, + ]; + " + `); }); diff --git a/src/__tests__/directives/long.test.js b/src/__tests__/directives/long.test.js index 9cf8923..1638827 100644 --- a/src/__tests__/directives/long.test.js +++ b/src/__tests__/directives/long.test.js @@ -41,62 +41,66 @@ test('thriftrw parses long and Long as numbers', () => { expect(thrift.MY_STRUCT.negNum3).toEqual(1); const converter = new ThriftFileConverter(fixturePath, false); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow + "// @flow -import thrift2flow$Long from \\"long\\"; + import thrift2flow$Long from 'long'; -export type MyStruct = {| - posNum1?: ?(number | thrift2flow$Long), - posNum2?: ?(number | thrift2flow$Long), - posNum3?: ?(number | Buffer), - negNum1?: ?(number | thrift2flow$Long), - negNum2?: ?(number | thrift2flow$Long), - negNum3?: ?(number | Buffer) -|}; + export type MyStruct = {| + posNum1?: ?(number | thrift2flow$Long), + posNum2?: ?(number | thrift2flow$Long), + posNum3?: ?(number | Buffer), + negNum1?: ?(number | thrift2flow$Long), + negNum2?: ?(number | thrift2flow$Long), + negNum3?: ?(number | Buffer), + |}; -export const MY_STRUCT: $ReadOnly = { - posNum1: 1, - posNum2: 1, - posNum3: 1, - negNum1: 1, - negNum2: 1, - negNum3: 1 -}; -" -`); + export const MY_STRUCT: $ReadOnly = { + 'posNum1': 1, + 'posNum2': 1, + 'posNum3': 1, + 'negNum1': 1, + 'negNum2': 1, + 'negNum3': 1, + }; + " + `); }); test('The `long` import is included from service definition', () => { const fixturePath = 'src/__tests__/fixtures/long-from-service.thrift'; const converter = new ThriftFileConverter(fixturePath, false); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow + "// @flow -import thrift2flow$Long from \\"long\\"; + import thrift2flow$Long from 'long'; -export type Validate = { - getStatus: ({| userUUID: string |}) => boolean, - getSummary: ({| - userUUID: string, - startTime: number | thrift2flow$Long, - endTime: number | thrift2flow$Long - |}) => string -}; -" -`); + export type Validate = { + getStatus: ({| + userUUID: string, + |}) => boolean, + getSummary: ({| + userUUID: string, + startTime: (number | thrift2flow$Long), + endTime: (number | thrift2flow$Long), + |}) => string, + }; + " + `); }); test('The `long` import is included from service definition on return', () => { const fixturePath = 'src/__tests__/fixtures/long-from-service-return.thrift'; const converter = new ThriftFileConverter(fixturePath, false); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow + "// @flow -import thrift2flow$Long from \\"long\\"; + import thrift2flow$Long from 'long'; -export type Validate = { - getStatus: ({| userUUID: string |}) => number | thrift2flow$Long -}; -" -`); + export type Validate = { + getStatus: ({| + userUUID: string, + |}) => (number | thrift2flow$Long), + }; + " + `); }); diff --git a/src/__tests__/enums.test.js b/src/__tests__/enums.test.js index d566910..94d75a3 100644 --- a/src/__tests__/enums.test.js +++ b/src/__tests__/enums.test.js @@ -38,39 +38,39 @@ test('thriftrw enums work in map constants', () => { expect(thrift.THE_ENUM_PROP.DEFAULT).toEqual('DEFAULT'); const converter = new ThriftFileConverter(fixturePath, false); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow - -export const THE_ENUM_PROP: $ReadOnly<{| - DEFAULT: \\"DEFAULT\\", - LOW: \\"LOW\\", - HIGH: \\"HIGH\\" -|}> = Object.freeze({ - DEFAULT: \\"DEFAULT\\", - LOW: \\"LOW\\", - HIGH: \\"HIGH\\" -}); - -export const THE_STRING_MAP: $ReadOnly<{| - \\"0\\": string, - \\"1\\": string, - \\"2\\": string -|}> = { - \\"0\\": \\"Some default string\\", - \\"1\\": \\"Some low string\\", - \\"2\\": \\"Some high string\\" -}; - -export const THE_STRING_KEY_MAP: $ReadOnly<{| - DEFAULT: string, - LOW: string, - HIGH: string -|}> = { - [THE_ENUM_PROP.DEFAULT]: \\"some other default\\", - [THE_ENUM_PROP.LOW]: \\"some other low\\", - [THE_ENUM_PROP.HIGH]: \\"some other high\\" -}; -" -`); + "// @flow + + export const THE_ENUM_PROP: $ReadOnly<{| + 'DEFAULT': 'DEFAULT', + 'LOW': 'LOW', + 'HIGH': 'HIGH', + |}> = Object.freeze({ + 'DEFAULT': 'DEFAULT', + 'LOW': 'LOW', + 'HIGH': 'HIGH', + }); + + export const THE_STRING_MAP: $ReadOnly<{| + '0': string, + '1': string, + '2': string, + |}> = { + '0': 'Some default string', + '1': 'Some low string', + '2': 'Some high string', + }; + + export const THE_STRING_KEY_MAP: $ReadOnly<{| + 'DEFAULT': string, + 'LOW': string, + 'HIGH': string, + |}> = { + [THE_ENUM_PROP.DEFAULT]: 'some other default', + [THE_ENUM_PROP.LOW]: 'some other low', + [THE_ENUM_PROP.HIGH]: 'some other high', + }; + " + `); }); test('thriftrw enums are strings not numbers', () => { @@ -106,30 +106,30 @@ test('typedefs of enums can be referenced from structs', () => { false ); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow - -export const EnumTypedef: $ReadOnly<{| - OK: \\"OK\\", - ERROR: \\"ERROR\\" -|}> = Object.freeze({ - OK: \\"OK\\", - ERROR: \\"ERROR\\" -}); - -export const MyEnum: $ReadOnly<{| - OK: \\"OK\\", - ERROR: \\"ERROR\\" -|}> = Object.freeze({ - OK: \\"OK\\", - ERROR: \\"ERROR\\" -}); - -export type MyStruct = {| - f_MyEnum: $Values, - f_EnumTypedef: $Values -|}; -" -`); + "// @flow + + export const EnumTypedef: $ReadOnly<{| + 'OK': 'OK', + 'ERROR': 'ERROR', + |}> = Object.freeze({ + 'OK': 'OK', + 'ERROR': 'ERROR', + }); + + export const MyEnum: $ReadOnly<{| + 'OK': 'OK', + 'ERROR': 'ERROR', + |}> = Object.freeze({ + 'OK': 'OK', + 'ERROR': 'ERROR', + }); + + export type MyStruct = {| + f_MyEnum: $Values, + f_EnumTypedef: $Values, + |}; + " + `); }); test('enums with typedefs', done => { diff --git a/src/__tests__/identifiers/false.test.js b/src/__tests__/identifiers/false.test.js index 87d94f2..bebd9c0 100644 --- a/src/__tests__/identifiers/false.test.js +++ b/src/__tests__/identifiers/false.test.js @@ -35,13 +35,15 @@ test('thriftrw enums work in map constants', () => { expect(thrift.MY_ACCESS.allowed).toEqual(false); const converter = new ThriftFileConverter(fixturePath, false); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow + "// @flow -export type Access = {| allowed?: ?boolean |}; + export type Access = {| + allowed?: ?boolean, + |}; -export const MY_ACCESS: $ReadOnly = { - allowed: false -}; -" -`); + export const MY_ACCESS: $ReadOnly = { + 'allowed': false, + }; + " + `); }); diff --git a/src/__tests__/services.test.js b/src/__tests__/services.test.js index a8de8fc..03e406e 100644 --- a/src/__tests__/services.test.js +++ b/src/__tests__/services.test.js @@ -69,13 +69,16 @@ test('Extending a service', () => { expect(converter.generateFlowFile()).toMatchInlineSnapshot(` "// @flow - import * as service from \\"./service\\"; + import * as service from './service'; export type ExtendingService = service.RealService; export type ExtendingServiceWithMethods = { - getNumberTwo: ({| a: string, what: boolean |}) => number, - ...service.RealService + getNumberTwo: ({| + a: string, + what: boolean, + |}) => number, + ...service.RealService, }; " `); diff --git a/src/__tests__/struct.test.js b/src/__tests__/struct.test.js index f245b70..4764e6a 100644 --- a/src/__tests__/struct.test.js +++ b/src/__tests__/struct.test.js @@ -32,14 +32,16 @@ test('structs and have enum properties', () => { ); const jsContent = converter.generateFlowFile(); expect(jsContent).toMatchInlineSnapshot(` -"// @flow + "// @flow -import * as common from \\"./a/common\\"; -import * as unrelated from \\"./unrelated\\"; + import * as common from './a/common'; + import * as unrelated from './unrelated'; -export type Foo = {| propA?: ?$Values |}; -" -`); + export type Foo = {| + propA?: ?$Values, + |}; + " + `); }); test('structs with optional properties', () => { @@ -49,11 +51,13 @@ test('structs with optional properties', () => { ); const jsContent = converter.generateFlowFile(); expect(jsContent).toMatchInlineSnapshot(` -"// @flow + "// @flow -export type MyStruct = {| a?: ?string |}; -" -`); + export type MyStruct = {| + a?: ?string, + |}; + " + `); }); test('unions in typedefs from transitive dependencies are referenced as types', () => { @@ -63,11 +67,13 @@ test('unions in typedefs from transitive dependencies are referenced as types', ); const jsContent = converter.generateFlowFile(); expect(jsContent).toMatchInlineSnapshot(` -"// @flow + "// @flow -import * as fileb from \\"./fileb\\"; + import * as fileb from './fileb'; -export type AStruct = {| prop?: ?$Values |}; -" -`); + export type AStruct = {| + prop?: ?$Values, + |}; + " + `); }); diff --git a/src/__tests__/typedefs.test.js b/src/__tests__/typedefs.test.js index 171afd1..05e806f 100644 --- a/src/__tests__/typedefs.test.js +++ b/src/__tests__/typedefs.test.js @@ -40,13 +40,13 @@ test('Long module is imported when needed', () => { false ); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow + "// @flow -import thrift2flow$Long from \\"long\\"; + import thrift2flow$Long from 'long'; -export type Long = number | thrift2flow$Long; -" -`); + export type Long = (number | thrift2flow$Long); + " + `); }); test('typedefs should reference enum types not value', () => { @@ -55,15 +55,15 @@ test('typedefs should reference enum types not value', () => { false ); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow + "// @flow -import * as base from \\"./base\\"; + import * as base from './base'; -export type TimeRangeByDayOfWeek = {| - [$Values]: base.TimeRange[] -|}; -" -`); + export type TimeRangeByDayOfWeek = {| + [$Values]: base.TimeRange[], + |}; + " + `); }); test('typedef Date', done => { flowResultTest( @@ -139,11 +139,11 @@ test('typedef reserved type', () => { fs.writeFileSync(p, `typedef string Symbol`); let output = new ThriftFileConverter(p, false).generateFlowFile(); expect(output).toMatchInlineSnapshot(` -"// @flow + "// @flow -export type _Symbol = string; -" -`); + export type _Symbol = string; + " + `); }); test('typedef long in global scope', () => { diff --git a/src/__tests__/unions.test.js b/src/__tests__/unions.test.js index 027574d..08753ec 100644 --- a/src/__tests__/unions.test.js +++ b/src/__tests__/unions.test.js @@ -34,19 +34,31 @@ test('Long module is imported when needed', () => { false ); expect(converter.generateFlowFile()).toMatchInlineSnapshot(` -"// @flow + "// @flow -import thrift2flow$Long from \\"long\\"; + import thrift2flow$Long from 'long'; -export type RawValue = - | {| type: \\"binaryValue\\", binaryValue: Buffer |} - | {| type: \\"boolValue\\", boolValue: boolean |} - | {| type: \\"doubleValue\\", doubleValue: number |} - | {| type: \\"int32Value\\", int32Value: number |} - | {| type: \\"int64Value\\", int64Value: number | thrift2flow$Long |} - | {| type: \\"stringValue\\", stringValue: string |}; -" -`); + export type RawValue = {| + type: \\"binaryValue\\", + binaryValue: Buffer, + |} | {| + type: \\"boolValue\\", + boolValue: boolean, + |} | {| + type: \\"doubleValue\\", + doubleValue: number, + |} | {| + type: \\"int32Value\\", + int32Value: number, + |} | {| + type: \\"int64Value\\", + int64Value: (number | thrift2flow$Long), + |} | {| + type: \\"stringValue\\", + stringValue: string, + |}; + " + `); }); test('unions', done => { diff --git a/src/main/convert.js b/src/main/convert.js index 4792420..dd00b8d 100644 --- a/src/main/convert.js +++ b/src/main/convert.js @@ -24,7 +24,6 @@ */ import thrift from './thriftrw'; -import prettier from 'prettier'; import path from 'path'; import {id} from './identifier'; import type { @@ -91,7 +90,7 @@ type Parsed = {| idls: {[filename: string]: {||}}, |}; -const longDeclaration = "import thrift2flow$Long from 'long'"; +const longDeclaration = "import thrift2flow$Long from 'long';"; export class ThriftFileConverter { thriftPath: string; @@ -99,6 +98,7 @@ export class ThriftFileConverter { withsource: boolean; ast: Ast; identifiersTable: {[key: string]: AstNode}; + indent: number; constructor(thriftPath: string, withsource: boolean, parsed?: Parsed) { this.thriftPath = path.resolve(thriftPath); @@ -107,6 +107,7 @@ export class ThriftFileConverter { this.ast = this.thrift.asts[this.thrift.filename]; this.initIdentifiersTable(); this.withsource = withsource; + this.indent = 0; } initIdentifiersTable() { @@ -150,7 +151,7 @@ export class ThriftFileConverter { ); } - generateFlowFile: (?boolean) => string = skipFormat => { + generateFlowFile: () => string = () => { const result = [ '// @flow', this.withsource && `// Source: ${this.thriftPath}`, @@ -158,11 +159,22 @@ export class ThriftFileConverter { ...this.ast.definitions.map(this.convertDefinitionToCode), ] .filter(Boolean) - .join('\n\n'); - return (skipFormat === true - ? result - : prettier.format(result, {parser: 'flow'}) - ).replace(`${longDeclaration};`, `/*:: ${longDeclaration}; */`); // use flow comment to avoid need for runtime installation of `long` package + .join('\n\n') + .replace(`${longDeclaration};`, `/*:: ${longDeclaration}; */`); // use flow comment to avoid need for runtime installation of `long` package + + return `${result}\n`; + }; + + withBlockIndent = (fn: () => T): T => { + this.indent++; + const res = fn(); + this.indent--; + + return res; + }; + + getIndent = (diff?: number): string => { + return ''.padStart(Math.max(0, this.indent + (diff || 0)) * 2, ' '); }; convertDefinitionToCode = (def: Definition) => { @@ -191,22 +203,35 @@ export class ThriftFileConverter { }; generateService = (def: Service) => { - const functions = def.functions.map(this.generateFunction).join(','); - if (def.baseService && def.baseService.name) { - if (def.functions.length === 0) { - return `export type ${id(def.id.name)} = ${def.baseService.name}`; + return this.withBlockIndent(() => { + const indent = this.getIndent(); + const functions = def.functions + .map(fn => this.generateFunction(fn, indent)) + .join('\n'); + if (def.baseService && def.baseService.name) { + if (!functions.length) { + return `export type ${id(def.id.name)} = ${def.baseService.name};`; + } + + return `export type ${id(def.id.name)} = {\n${functions}\n${indent}...${ + def.baseService.name + },\n${this.getIndent(-1)}};`; } - return `export type ${id(def.id.name)} = {\n${functions}, ...${ - def.baseService.name - }};`; - } - return `export type ${id(def.id.name)} = {${functions}};`; + + if (!functions.length) { + return `export type ${id(def.id.name)} = {};`; + } + + return `export type ${id( + def.id.name + )} = {\n${functions}\n${this.getIndent(-1)}};`; + }); }; - generateFunction = (fn: FunctionDefinition) => - `${fn.id.name}: (${ + generateFunction = (fn: FunctionDefinition, indent: string) => + `${indent}${fn.id.name}: (${ fn.fields.length ? this.generateStructContents([...fn.fields]) : '' - }) => ${this.convertType(fn.returns)}`; + }) => ${this.convertType(fn.returns)},`; generateTypedef = (def: Typedef) => { if (def.valueType.type === 'Identifier') { @@ -225,16 +250,17 @@ export class ThriftFileConverter { }; generateEnum = (def: Enum, otherName?: string) => { - const values = def.definitions - .map((d, index) => `'${d.id.name}': '${d.id.name}',`) - .join('\n'); - return `export const ${ - otherName !== undefined ? otherName : def.id.name - }: $ReadOnly<{| - ${values} -|}> = Object.freeze({ - ${values} -});`; + return this.withBlockIndent(() => { + const indent = this.getIndent(); + const values = def.definitions + .map((d, index) => `${indent}'${d.id.name}': '${d.id.name}',`) + .join('\n'); + return `export const ${ + otherName !== undefined ? otherName : def.id.name + }: $ReadOnly<{|\n${values}\n|}> = Object.freeze({\n${values}\n${this.getIndent( + -1 + )}});`; + }); }; generateConst = (def: Const) => { @@ -242,33 +268,41 @@ export class ThriftFileConverter { let enumType: ?string; let readOnly = false; if (def.value.type === 'ConstList') { - value = `[${def.value.values - .map((val: Identifier | Literal) => { - if (val.type === 'Identifier') { - if (val.name.includes('.')) { - const {definition} = this.definitionOfIdentifier( - val.name, - this.thrift.filename - ); - if (definition.type == 'EnumDefinition') { - const scope = val.name.split('.')[0]; - const defAndFilename = this.definitionOfIdentifier( - scope, + if (!def.value.values.length) { + return '[]'; + } + const listValues = def.value.values; + + value = this.withBlockIndent(() => { + const indent = this.getIndent(); + return `[\n${listValues + .map((val: Identifier | Literal) => { + if (val.type === 'Identifier') { + if (val.name.includes('.')) { + const {definition} = this.definitionOfIdentifier( + val.name, this.thrift.filename ); - if (enumType === undefined && this.isEnum(defAndFilename)) { - enumType = `${this.getIdentifier(scope, 'type')}[]`; + if (definition.type == 'EnumDefinition') { + const scope = val.name.split('.')[0]; + const defAndFilename = this.definitionOfIdentifier( + scope, + this.thrift.filename + ); + if (enumType === undefined && this.isEnum(defAndFilename)) { + enumType = `${this.getIdentifier(scope, 'type')}[]`; + } } } + return `${indent}${this.getIdentifier(val.name, 'value')},`; } - return this.getIdentifier(val.name, 'value'); - } - if (val.type === 'Literal' && typeof val.value === 'string') { - return `'${val.value}'`; - } - return val.value; - }) - .join(',')}]`; + if (val.type === 'Literal' && typeof val.value === 'string') { + return `${indent}'${val.value}',`; + } + return val.value; + }) + .join('\n')}\n${this.getIndent(-1)}]`; + }); } else { if (def.value.type === 'Literal') { if (typeof def.value.value === 'string') { @@ -308,17 +342,24 @@ export class ThriftFileConverter { } = ${value};`; }; - generateConstList(def: ConstList) { - return `[${def.values - .map(entry => { - if (entry.type === 'Identifier') { - return entry.name; - } else { - return `"${entry.value}"`; - } - }) - .join(',')}]`; - } + generateConstList = (def: ConstList) => { + return this.withBlockIndent(() => { + if (!def.values.length) { + return '[]'; + } + + const indent = this.getIndent(); + return `[\n${def.values + .map(entry => { + if (entry.type === 'Identifier') { + return `${indent}${entry.name},`; + } else { + return `${indent}"${entry.value}",`; + } + }) + .join('\n')}\n${this.getIndent(-1)}]`; + }); + }; generateConstEntry = (entry: ConstEntry) => { let key; @@ -366,45 +407,65 @@ export class ThriftFileConverter { console.error(entry); throw new Error(`key or value is undefined`); } - const result = `${key}: ${value},`; + const result = `${this.getIndent()}${key}: ${value},`; return result; }; generateConstMap = (def: ConstMap) => { - return `{ - ${def.entries.map(entry => this.generateConstEntry(entry)).join('\n')} - } `; + return this.withBlockIndent(() => { + if (!def.entries.length) { + return '{}'; + } + + return `{\n${def.entries + .map(entry => this.generateConstEntry(entry)) + .join('\n')}\n${this.getIndent(-1)}}`; + }); }; generateStruct = ({id: {name}, fields}: Struct | Exception) => `export type ${id(name)} = ${this.generateStructContents(fields)};`; - generateStructContents = (fields: Array) => - `{|${fields - .map((field: Field) => { - const valueType = field.valueType; - let optionalPrefix = this.isOptional(field) ? '?' : ''; - let value = - valueType.type === 'Identifier' - ? this.getIdentifier(valueType.name, 'type') - : this.convertType(valueType); - return `${field.name}${optionalPrefix}: ${optionalPrefix}${value};`; - }) - .join('\n')}|}`; + generateStructContents = (fields: Array) => { + return this.withBlockIndent(() => { + if (!fields.length) { + return '{}'; + } + + const indent = this.getIndent(); + return `{|\n${fields + .map((field: Field) => { + const valueType = field.valueType; + let optionalPrefix = this.isOptional(field) ? '?' : ''; + let value = + valueType.type === 'Identifier' + ? this.getIdentifier(valueType.name, 'type') + : this.convertType(valueType); + return `${indent}${field.name}${optionalPrefix}: ${optionalPrefix}${value},`; + }) + .join('\n')}\n${this.getIndent(-1)}|}`; + }); + }; generateUnion = ({id: {name}, fields}: Union) => `export type ${id(name)} = ${this.generateUnionContents(fields)};`; generateUnionContents = (fields: Array) => { - if (!fields.length) { - return '{||}'; - } - return fields - .map( - (f: Field) => - `{|type: "${f.name}",${f.name}: ${this.convertType(f.valueType)}|}` - ) - .join(' | '); + return this.withBlockIndent(() => { + if (!fields.length) { + return '{||}'; + } + + const indent = this.getIndent(); + return fields + .map( + (f: Field) => + `{|\n${indent}type: "${f.name}",\n${indent}${ + f.name + }: ${this.convertType(f.valueType)},\n${this.getIndent(-1)}|}` + ) + .join(' | '); + }); }; isOptional = (field: Field) => field.optional; @@ -623,41 +684,52 @@ export class ThriftFileConverter { } convertMapType(t: AstNode, def?: Definition): string | void { - if (t.type === 'Map' && def) { - const valueType = this.convertType(t.valueType); - if (def.type === 'Const' && def.value.type === 'ConstMap') { - const entries = def.value.entries.map(entry => { - if (entry.key.type === 'Identifier') { - const identifierValue: AstNode = this.identifiersTable[ - entry.key.name - ]; - if (identifierValue.type === 'EnumDefinition') { - return `'${identifierValue.id.name}': ${valueType}`; - } else if ( - identifierValue.type === 'Const' && - typeof identifierValue.value.value === 'string' - ) { - return `'${identifierValue.value.value}': ${valueType}`; + return this.withBlockIndent(() => { + if (t.type === 'Map' && def) { + const valueType = this.convertType(t.valueType); + if (def.type === 'Const' && def.value.type === 'ConstMap') { + if (!def.value.entries.length) { + return '{}'; + } + const mapEntries = def.value.entries; + + const indent = this.getIndent(); + const entries = mapEntries.map(entry => { + if (entry.key.type === 'Identifier') { + const identifierValue: AstNode = this.identifiersTable[ + entry.key.name + ]; + if (identifierValue.type === 'EnumDefinition') { + return `${indent}'${identifierValue.id.name}': ${valueType},`; + } else if ( + identifierValue.type === 'Const' && + typeof identifierValue.value.value === 'string' + ) { + return `${indent}'${identifierValue.value.value}': ${valueType},`; + } else { + throw new Error( + `Unknown identifierValue type ${identifierValue.type}` + ); + } + } else if (entry.key.type === 'Literal') { + return `${indent}'${entry.key.value}': ${valueType},`; } else { - throw new Error( - `Unknown identifierValue type ${identifierValue.type}` - ); + throw new Error('unsupported'); } - } else if (entry.key.type === 'Literal') { - return `'${entry.key.value}': ${valueType}`; - } else { - throw new Error('unsupported'); - } - }); - return `{| ${entries.join(',')} |}`; + }); + return `{|\n${entries.join('\n')}\n${this.getIndent(-1)}|}`; + } } - } - if (t.type === 'Map') { - const keyType = this.convertType(t.keyType); - const valueType = this.convertType(t.valueType); - return `{| [${keyType}]: ${valueType} |}`; - } - return undefined; + if (t.type === 'Map') { + const keyType = this.convertType(t.keyType); + const valueType = this.convertType(t.valueType); + const indent = this.getIndent(); + return `{|\n${indent}[${keyType}]: ${valueType},\n${this.getIndent( + -1 + )}|}`; + } + return undefined; + }); } isEnumIdentifier(def: AstNode) { diff --git a/src/main/index.js b/src/main/index.js index 6c9bbd1..dba3103 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -8,7 +8,6 @@ type OptionsType = {| withSource?: boolean, commonPath: string, outputDir?: string, - skipFormat?: boolean, |}; type Parsed = {| @@ -40,7 +39,7 @@ export default function convert( relativeThriftPath, `${path.basename(thriftPath, '.thrift')}.js` ); - allOutput[jsFilename] = converter.generateFlowFile(options.skipFormat); + allOutput[jsFilename] = converter.generateFlowFile(); } return allOutput; } @@ -68,7 +67,7 @@ export function convertParsed(thriftParsed: Parsed, options: OptionsType) { relativeThriftPath, `${path.basename(thriftPath, '.thrift')}.js` ); - allOutput[jsFilename] = converter.generateFlowFile(options.skipFormat); + allOutput[jsFilename] = converter.generateFlowFile(); } return allOutput; }