From d22316882b53c0d84991bc9884240d7553609dc0 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Sat, 5 Jan 2019 09:53:48 +0000 Subject: [PATCH 1/3] Add prettier --- package.json | 3 +++ prettier.config.js | 6 ++++++ yarn.lock | 5 +++++ 3 files changed, 14 insertions(+) create mode 100644 prettier.config.js diff --git a/package.json b/package.json index 28a0281..4aff330 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "build:watch": "tsc -w", "clean": "rm -rf ./dist", "lint": "tslint src/**/*.ts", + "prettier": "prettier 'src/**/*.[tj]s'", + "prettier:fix": "yarn prettier --write", "prepublishOnly": "npm run clean && npm run lint && npm run build -- -d", "pretest": "npm run build", "tdd": "concurrently -k 'npm run build:watch' 'npm run test:watch'", @@ -38,6 +40,7 @@ "ava": "^0.24.0", "concurrently": "^3.5.1", "flow-bin": "^0.59.0", + "prettier": "^1.15.3", "tslint": "^5.8.0", "typescript": "^2.6.2" }, diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..7e10ad0 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,6 @@ +module.exports = { + printWidth: 80, + semi: false, + singleQuote: true, + trailingComma: 'none', +} diff --git a/yarn.lock b/yarn.lock index e760061..45ffd62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2315,6 +2315,11 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prettier@^1.15.3: + version "1.15.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.3.tgz#1feaac5bdd181237b54dbe65d874e02a1472786a" + integrity sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg== + pretty-ms@^0.2.1: version "0.2.2" resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-0.2.2.tgz#da879a682ff33a37011046f13d627f67c73b84f6" From d9699a7574703cb5f3c0fb8a076c32b07f34b36e Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Sat, 5 Jan 2019 09:54:48 +0000 Subject: [PATCH 2/3] Autoformat with 'yarn prettier:fix' --- src/cli.ts | 20 ++--- src/convert.ts | 176 ++++++++++++++++++++++++++++------------ src/index.ts | 26 ++++-- src/rules/$Exact.ts | 1 - src/rules/$Keys.ts | 8 +- src/rules/$ReadOnly.ts | 8 +- src/rules/Bounds.ts | 8 +- src/rules/Exact.ts | 12 +-- src/rules/Indexer.ts | 14 ++-- src/rules/Maybe.ts | 19 +++-- src/rules/Opaque.ts | 12 +-- src/rules/TypeImport.ts | 4 +- 12 files changed, 202 insertions(+), 106 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index ab5aaa7..66a5921 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -6,16 +6,17 @@ import { resolve } from 'path' import stdin = require('stdin') import { compile } from './index' -main(minimist(process.argv.slice(2), { - alias: { - help: ['h'], - input: ['i'], - output: ['o'] - } -})) +main( + minimist(process.argv.slice(2), { + alias: { + help: ['h'], + input: ['i'], + output: ['o'] + } + }) +) async function main(argv: minimist.ParsedArgs) { - if (argv.help) { printHelp() process.exit(0) @@ -32,7 +33,6 @@ async function main(argv: minimist.ParsedArgs) { process.stderr.write(e.message) process.exit(1) } - } function readInput(argIn?: string) { @@ -58,7 +58,7 @@ function printHelp() { const pkg = require('../../package.json') process.stdout.write( -` + ` ${pkg.name} ${pkg.version} Usage: flow2ts [--input, -i] [IN_FILE] [--output, -o] [OUT_FILE] diff --git a/src/convert.ts b/src/convert.ts index 881474f..9fcf45c 100644 --- a/src/convert.ts +++ b/src/convert.ts @@ -1,10 +1,48 @@ -import { booleanLiteral, Flow, FlowType, FunctionTypeAnnotation, identifier, Identifier, isSpreadProperty, isTSTypeParameter, isTypeParameter, Node, numericLiteral, stringLiteral, tsAnyKeyword, tsArrayType, tsAsExpression, tsBooleanKeyword, tsFunctionType, TSFunctionType, tsIntersectionType, tsLiteralType, tsNullKeyword, tsNumberKeyword, tsPropertySignature, tsStringKeyword, tsThisType, tsTupleType, TSType, tsTypeAnnotation, tsTypeLiteral, tsTypeParameter, tsTypeParameterDeclaration, tsTypeQuery, tsTypeReference, tsUndefinedKeyword, tsUnionType, tsVoidKeyword, TypeAnnotation, TypeParameter } from '@babel/types' +import { + booleanLiteral, + Flow, + FlowType, + FunctionTypeAnnotation, + identifier, + Identifier, + isSpreadProperty, + isTSTypeParameter, + isTypeParameter, + Node, + numericLiteral, + stringLiteral, + tsAnyKeyword, + tsArrayType, + tsAsExpression, + tsBooleanKeyword, + tsFunctionType, + TSFunctionType, + tsIntersectionType, + tsLiteralType, + tsNullKeyword, + tsNumberKeyword, + tsPropertySignature, + tsStringKeyword, + tsThisType, + tsTupleType, + TSType, + tsTypeAnnotation, + tsTypeLiteral, + tsTypeParameter, + tsTypeParameterDeclaration, + tsTypeQuery, + tsTypeReference, + tsUndefinedKeyword, + tsUnionType, + tsVoidKeyword, + TypeAnnotation, + TypeParameter +} from '@babel/types' import { generateFreeIdentifier } from './utils' // TODO: Add overloads export function toTs(node: Flow | TSType): TSType { switch (node.type) { - // TS types // TODO: Why does tsTs get called with TSTypes? It should only get called with Flow types. case 'TSAnyKeyword': @@ -75,7 +113,7 @@ export function toTs(node: Flow | TSType): TSType { case 'TypeParameterDeclaration': let params = node.params.map(_ => { - let d = (_ as any as TypeParameter).default + let d = ((_ as any) as TypeParameter).default let p = tsTypeParameter( hasBound(_) ? toTsType(_.bound.typeAnnotation) : undefined, d ? toTs(d) : undefined @@ -109,68 +147,97 @@ export function toTs(node: Flow | TSType): TSType { export function toTsType(node: FlowType): TSType { switch (node.type) { - case 'AnyTypeAnnotation': return tsAnyKeyword() - case 'ArrayTypeAnnotation': return tsArrayType(toTsType(node.elementType)) - case 'BooleanTypeAnnotation': return tsBooleanKeyword() - case 'BooleanLiteralTypeAnnotation': return tsLiteralType(booleanLiteral(node.value!)) - case 'FunctionTypeAnnotation': return functionToTsType(node) - case 'GenericTypeAnnotation': return tsTypeReference(node.id) - case 'IntersectionTypeAnnotation': return tsIntersectionType(node.types.map(toTsType)) - case 'MixedTypeAnnotation': return tsAnyKeyword() - case 'NullLiteralTypeAnnotation': return tsNullKeyword() - case 'NullableTypeAnnotation': return tsUnionType([toTsType(node.typeAnnotation), tsNullKeyword(), tsUndefinedKeyword()]) - case 'NumberLiteralTypeAnnotation': return tsLiteralType(numericLiteral(node.value!)) - case 'NumberTypeAnnotation': return tsNumberKeyword() - case 'StringLiteralTypeAnnotation': return tsLiteralType(stringLiteral(node.value!)) - case 'StringTypeAnnotation': return tsStringKeyword() - case 'ThisTypeAnnotation': return tsThisType() - case 'TupleTypeAnnotation': return tsTupleType(node.types.map(toTsType)) - case 'TypeofTypeAnnotation': return tsTypeQuery(getId(node.argument)) - case 'ObjectTypeAnnotation': return tsTypeLiteral([ - ...node.properties.map(_ => { - if (isSpreadProperty(_)) { - return _ - } - let s = tsPropertySignature(_.key, tsTypeAnnotation(toTsType(_.value))) - s.optional = _.optional - return s - // TODO: anonymous indexers - // TODO: named indexers - // TODO: call properties - // TODO: variance - }) - // ...node.indexers.map(_ => tSIndexSignature()) - ]) - case 'UnionTypeAnnotation': return tsUnionType(node.types.map(toTsType)) - case 'VoidTypeAnnotation': return tsVoidKeyword() + case 'AnyTypeAnnotation': + return tsAnyKeyword() + case 'ArrayTypeAnnotation': + return tsArrayType(toTsType(node.elementType)) + case 'BooleanTypeAnnotation': + return tsBooleanKeyword() + case 'BooleanLiteralTypeAnnotation': + return tsLiteralType(booleanLiteral(node.value!)) + case 'FunctionTypeAnnotation': + return functionToTsType(node) + case 'GenericTypeAnnotation': + return tsTypeReference(node.id) + case 'IntersectionTypeAnnotation': + return tsIntersectionType(node.types.map(toTsType)) + case 'MixedTypeAnnotation': + return tsAnyKeyword() + case 'NullLiteralTypeAnnotation': + return tsNullKeyword() + case 'NullableTypeAnnotation': + return tsUnionType([ + toTsType(node.typeAnnotation), + tsNullKeyword(), + tsUndefinedKeyword() + ]) + case 'NumberLiteralTypeAnnotation': + return tsLiteralType(numericLiteral(node.value!)) + case 'NumberTypeAnnotation': + return tsNumberKeyword() + case 'StringLiteralTypeAnnotation': + return tsLiteralType(stringLiteral(node.value!)) + case 'StringTypeAnnotation': + return tsStringKeyword() + case 'ThisTypeAnnotation': + return tsThisType() + case 'TupleTypeAnnotation': + return tsTupleType(node.types.map(toTsType)) + case 'TypeofTypeAnnotation': + return tsTypeQuery(getId(node.argument)) + case 'ObjectTypeAnnotation': + return tsTypeLiteral([ + ...node.properties.map(_ => { + if (isSpreadProperty(_)) { + return _ + } + let s = tsPropertySignature( + _.key, + tsTypeAnnotation(toTsType(_.value)) + ) + s.optional = _.optional + return s + // TODO: anonymous indexers + // TODO: named indexers + // TODO: call properties + // TODO: variance + }) + // ...node.indexers.map(_ => tSIndexSignature()) + ]) + case 'UnionTypeAnnotation': + return tsUnionType(node.types.map(toTsType)) + case 'VoidTypeAnnotation': + return tsVoidKeyword() } } function getId(node: FlowType): Identifier { switch (node.type) { - case 'GenericTypeAnnotation': return node.id - default: throw ReferenceError('typeof query must reference a node that has an id') + case 'GenericTypeAnnotation': + return node.id + default: + throw ReferenceError('typeof query must reference a node that has an id') } } function functionToTsType(node: FunctionTypeAnnotation): TSFunctionType { - let typeParams = undefined if (node.typeParameters) { - typeParams = tsTypeParameterDeclaration(node.typeParameters.params.map(_ => { - - // TODO: How is this possible? - if (isTSTypeParameter(_)) { - return _ - } + typeParams = tsTypeParameterDeclaration( + node.typeParameters.params.map(_ => { + // TODO: How is this possible? + if (isTSTypeParameter(_)) { + return _ + } - let constraint = _.bound ? toTs(_.bound) : undefined - let default_ = _.default ? toTs(_.default) : undefined - let param = tsTypeParameter(constraint, default_) - param.name = _.name - return param - })) + let constraint = _.bound ? toTs(_.bound) : undefined + let default_ = _.default ? toTs(_.default) : undefined + let param = tsTypeParameter(constraint, default_) + param.name = _.name + return param + }) + ) } let f = tsFunctionType(typeParams) @@ -178,7 +245,10 @@ function functionToTsType(node: FunctionTypeAnnotation): TSFunctionType { // Params if (node.params) { // TODO: Rest params - let paramNames = node.params.map(_ => _.name).filter(_ => _ !== null).map(_ => (_ as Identifier).name) + let paramNames = node.params + .map(_ => _.name) + .filter(_ => _ !== null) + .map(_ => (_ as Identifier).name) f.parameters = node.params.map(_ => { let name = _.name && _.name.name diff --git a/src/index.ts b/src/index.ts index a4003f9..b81c00f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,30 +20,38 @@ export function addRule(ruleName: string, rule: Rule) { } export async function compile(code: string, filename: string) { - let [warnings, ast] = await convert( - parse(code, { plugins: ['classProperties', 'flow', 'objectRestSpread'], sourceType: 'module' }) + parse(code, { + plugins: ['classProperties', 'flow', 'objectRestSpread'], + sourceType: 'module' + }) ) warnings.forEach(([message, issueURL, line, column]) => { - console.log(`Warning: ${message} (at ${relative(__dirname, filename)}: line ${line}, column ${column}). See ${issueURL}`) + console.log( + `Warning: ${message} (at ${relative( + __dirname, + filename + )}: line ${line}, column ${column}). See ${issueURL}` + ) }) - return addTrailingSpace(trimLeadingNewlines(generate(stripAtFlowAnnotation(ast)).code)) + return addTrailingSpace( + trimLeadingNewlines(generate(stripAtFlowAnnotation(ast)).code) + ) } /** * @internal */ export async function convert(ast: T): Promise<[Warning[], T]> { - // load rules directory - await Promise.all(sync(resolve(__dirname, './rules/*.js')).map(_ => import(_))) + await Promise.all( + sync(resolve(__dirname, './rules/*.js')).map(_ => import(_)) + ) let warnings: Warning[] = [] - rules.forEach(visitor => - traverse(ast, visitor(warnings)) - ) + rules.forEach(visitor => traverse(ast, visitor(warnings))) return [warnings, ast] } diff --git a/src/rules/$Exact.ts b/src/rules/$Exact.ts index e9d3a6b..a84c129 100644 --- a/src/rules/$Exact.ts +++ b/src/rules/$Exact.ts @@ -2,7 +2,6 @@ import { addRule } from '../' addRule('$Exact', warnings => ({ GenericTypeAnnotation(path) { - if (path.node.id.name !== '$Exact') { return } diff --git a/src/rules/$Keys.ts b/src/rules/$Keys.ts index 805d015..69bd057 100644 --- a/src/rules/$Keys.ts +++ b/src/rules/$Keys.ts @@ -1,4 +1,8 @@ -import { GenericTypeAnnotation, tsTypeOperator, tsTypeReference } from '@babel/types' +import { + GenericTypeAnnotation, + tsTypeOperator, + tsTypeReference +} from '@babel/types' import { addRule } from '../' addRule('$Keys', () => ({ @@ -6,7 +10,7 @@ addRule('$Keys', () => ({ if (path.node.id.name !== '$Keys') { return } - let { id } = (path.node.typeParameters.params[0] as GenericTypeAnnotation) + let { id } = path.node.typeParameters.params[0] as GenericTypeAnnotation let op = tsTypeOperator(tsTypeReference(id)) path.replaceWith(op) } diff --git a/src/rules/$ReadOnly.ts b/src/rules/$ReadOnly.ts index bcd0095..5cd92fd 100644 --- a/src/rules/$ReadOnly.ts +++ b/src/rules/$ReadOnly.ts @@ -3,14 +3,12 @@ import { addRule } from '../' addRule('$ReadOnly', () => ({ GenericTypeAnnotation(path) { - if (path.node.id.name !== '$ReadOnly') { return } - path.replaceWith(genericTypeAnnotation( - identifier('Readonly'), - path.node.typeParameters - )) + path.replaceWith( + genericTypeAnnotation(identifier('Readonly'), path.node.typeParameters) + ) } })) diff --git a/src/rules/Bounds.ts b/src/rules/Bounds.ts index 7bc0b43..22bf009 100644 --- a/src/rules/Bounds.ts +++ b/src/rules/Bounds.ts @@ -1,10 +1,14 @@ -import { isTypeParameter, Node, TypeAnnotation, TypeParameter } from '@babel/types' +import { + isTypeParameter, + Node, + TypeAnnotation, + TypeParameter +} from '@babel/types' import { addRule } from '../' import { toTs } from '../convert' addRule('Bounds', () => ({ TypeParameterDeclaration(path) { - if (path.node.params.every(_ => !hasBound(_))) { return } diff --git a/src/rules/Exact.ts b/src/rules/Exact.ts index f33a19a..321757e 100644 --- a/src/rules/Exact.ts +++ b/src/rules/Exact.ts @@ -10,11 +10,13 @@ addRule('Exact', warnings => ({ path.node.loc.start.line, path.node.loc.start.column ]) - path.replaceWith(objectTypeAnnotation( - path.node.properties, - path.node.indexers, - path.node.callProperties - )) + path.replaceWith( + objectTypeAnnotation( + path.node.properties, + path.node.indexers, + path.node.callProperties + ) + ) } } })) diff --git a/src/rules/Indexer.ts b/src/rules/Indexer.ts index 1f2a9d9..6dff97c 100644 --- a/src/rules/Indexer.ts +++ b/src/rules/Indexer.ts @@ -4,16 +4,16 @@ import { generateFreeIdentifier } from '../utils' addRule('Indexer', () => ({ ObjectTypeIndexer(path) { - if (path.node.id !== null) { return } - path.replaceWith(objectTypeIndexer( - identifier(generateFreeIdentifier([])), - path.node.key, - path.node.value - )) - + path.replaceWith( + objectTypeIndexer( + identifier(generateFreeIdentifier([])), + path.node.key, + path.node.value + ) + ) } })) diff --git a/src/rules/Maybe.ts b/src/rules/Maybe.ts index 369f514..52ddf1e 100644 --- a/src/rules/Maybe.ts +++ b/src/rules/Maybe.ts @@ -1,12 +1,19 @@ -import { genericTypeAnnotation, identifier, nullLiteralTypeAnnotation, unionTypeAnnotation } from '@babel/types' +import { + genericTypeAnnotation, + identifier, + nullLiteralTypeAnnotation, + unionTypeAnnotation +} from '@babel/types' import { addRule } from '../' addRule('Maybe', () => ({ NullableTypeAnnotation(path) { - path.replaceWith(unionTypeAnnotation([ - (path.node as any).typeAnnotation, - nullLiteralTypeAnnotation(), - genericTypeAnnotation(identifier('undefined')) - ])) + path.replaceWith( + unionTypeAnnotation([ + (path.node as any).typeAnnotation, + nullLiteralTypeAnnotation(), + genericTypeAnnotation(identifier('undefined')) + ]) + ) } })) diff --git a/src/rules/Opaque.ts b/src/rules/Opaque.ts index af1da81..eb0b2ea 100644 --- a/src/rules/Opaque.ts +++ b/src/rules/Opaque.ts @@ -10,11 +10,13 @@ addRule('Opaque', warnings => ({ path.node.loc.start.line, path.node.loc.start.column ]) - path.replaceWith(typeAlias( - (path.node as any).id, - (path.node as any).typeParameters, - (path.node as any).impltype - )) + path.replaceWith( + typeAlias( + (path.node as any).id, + (path.node as any).typeParameters, + (path.node as any).impltype + ) + ) } } })) diff --git a/src/rules/TypeImport.ts b/src/rules/TypeImport.ts index ad9ff67..f477fa3 100644 --- a/src/rules/TypeImport.ts +++ b/src/rules/TypeImport.ts @@ -4,7 +4,9 @@ import { addRule } from '../' addRule('TypeImport', () => ({ ImportDeclaration(path) { if ((path as any).node.importKind === 'type') { - path.replaceWith(importDeclaration(path.node.specifiers, path.node.source)) + path.replaceWith( + importDeclaration(path.node.specifiers, path.node.source) + ) } } })) From 85fce70d19f2e013a134afc4b3cf0b3f0a2dfa15 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Sat, 5 Jan 2019 10:09:32 +0000 Subject: [PATCH 3/3] Assert prettier --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4aff330..a6278ee 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,10 @@ "build": "tsc", "build:watch": "tsc -w", "clean": "rm -rf ./dist", - "lint": "tslint src/**/*.ts", + "lint": "tslint src/**/*.ts && npm run prettier:check", "prettier": "prettier 'src/**/*.[tj]s'", "prettier:fix": "yarn prettier --write", + "prettier:check": "yarn prettier -l", "prepublishOnly": "npm run clean && npm run lint && npm run build -- -d", "pretest": "npm run build", "tdd": "concurrently -k 'npm run build:watch' 'npm run test:watch'",