Skip to content

Commit

Permalink
fix: almost all remaining issues
Browse files Browse the repository at this point in the history
  • Loading branch information
jedwards1211 committed Jan 8, 2021
1 parent 6654a7c commit 9d6e02f
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 58 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"@commitlint/config-conventional": "^11.0.0",
"@jedwards1211/commitlint-config": "^1.0.1",
"@jedwards1211/eslint-config-typescript": "^1.0.0",
"@types/babel__generator": "^7.6.2",
"@types/babel__template": "^7.4.0",
"@types/babel__traverse": "^7.11.0",
"@types/chai": "^4.2.0",
Expand Down
2 changes: 1 addition & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ async function go() {
}
for (const [file, ast] of context.fileASTs.entries()) {
const prettierOptions = {
parser: /\.tsx?$/.test(file) ? 'typescript' : 'babel',
parser: /\.tsx?$/.test(file) ? 'typescript' : 'babel-flow',
}
const printed = prettier.format(recast.print(ast).code, prettierOptions)
const orig = prettier.format(
Expand Down
154 changes: 97 additions & 57 deletions src/convert/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ import moveImportKindToSpecifiers from './moveImportKindToSpecifiers'
import areReferencesEqual from './areReferencesEqual'
import getKey from './getKey'
import convertUtilityFlowType from './convertUtilityFlowType'
import moveLeadingCommentsToNextSibling from './moveCommentsToNextSibling'

const templates = {
importTypedValidators: template.statement`import * as T from 'typed-validators'`,
importValidation: template.statement`import { Validation as LOCAL } from 'typed-validators'`,
undefined: template.expression`T.undefined()`,
any: template.expression`T.any()`,
unknown: template.expression`T.unknown()`,
null: template.expression`T.null()`,
number: template.expression`T.number()`,
numberLiteral: template.expression`T.number(VALUE)`,
Expand Down Expand Up @@ -144,9 +147,6 @@ export class FileConversionContext {
| NodePath<t.TSAsExpression>
)[] = []
traverse(ast, {
ImportDeclaration: (path: NodePath<t.ImportDeclaration>) => {
if (path.node.source.value === 'flow-runtime') path.remove()
},
TypeCastExpression: (path: NodePath<t.TypeCastExpression>) => {
if (getReifiedType(path)) {
reifyCalls.push(path)
Expand All @@ -161,41 +161,74 @@ export class FileConversionContext {
},
})
for (const path of reifyCalls) {
const reifiedType = getReifiedType(path)
if (reifiedType) {
const id = reifiedType.isGenericTypeAnnotation()
? (reifiedType as NodePath<t.GenericTypeAnnotation>).get('id')
: reifiedType.isTSTypeReference()
? (reifiedType as NodePath<t.TSTypeReference>).get('typeName')
: null
let converted
if (id?.isIdentifier()) {
id.scope.path.traverse(TSBindingVisitors)
const binding = id.scope.getBinding(id.node.name)
if (binding && binding.path.isTypeAlias()) {
converted = await this.convert(
(binding.path as NodePath<t.TypeAlias>).get('right')
)
} else if (binding && binding.path.isTSTypeAliasDeclaration()) {
converted = await this.convert(
(binding.path as NodePath<t.TSTypeAliasDeclaration>).get(
'typeAnnotation'
await this.replaceReifyCall(path)
}
traverse(ast, {
ImportDeclaration: (path: NodePath<t.ImportDeclaration>) => {
if (path.node.source.value === 'flow-runtime') {
const validationImport = path.node.specifiers
? path.node.specifiers.find(
s =>
s.type === 'ImportSpecifier' &&
s.imported.type === 'Identifier' &&
s.imported.name === 'Validation'
)
)
: null
if (validationImport) {
const newValidationImport = templates.importValidation({
LOCAL: validationImport.local,
}) as any

newValidationImport.importKind =
(validationImport.type === 'ImportSpecifier'
? validationImport.importKind
: null) || path.node.importKind
path.insertAfter(newValidationImport)
}
}
const finalConverted = converted || (await this.convert(reifiedType))
if (
id?.isIdentifier() &&
finalConverted.type === 'Identifier' &&
id.node.name === finalConverted.name
) {
moveLeadingCommentsToNextSibling(path)
path.remove()
} else {
path.replaceWith(finalConverted)
}
},
})
}

async replaceReifyCall(
path: NodePath<t.TypeCastExpression> | NodePath<t.TSAsExpression>
): Promise<void> {
const reifiedType = getReifiedType(path)
if (!reifiedType) return
if (reifiedType.isGenericTypeAnnotation()) {
const genType = reifiedType as NodePath<t.GenericTypeAnnotation>
const convertedUtility = await convertUtilityFlowType(this, genType)
if (convertedUtility) {
path.replaceWith(convertedUtility)
return
}
try {
const { converted, kind } = await this.convertTypeReference(
genType.get('id')
)
if (kind === 'class') {
path.replaceWith(
await this.convertTypeReferenceToValidator({ converted, kind })
)
return
}
const { parentPath } = path
if (parentPath.isVariableDeclarator()) {
const { id } = parentPath.node as t.VariableDeclarator
if (id.type === 'Identifier' && areReferencesEqual(converted, id)) {
parentPath.remove()
return
}
}
path.replaceWith(converted)
return
} catch (error) {
// ignore
}
}
path.replaceWith(await this.convert(reifiedType))
}

importT = once(
Expand Down Expand Up @@ -343,23 +376,24 @@ export class FileConversionContext {
return { converted: type.id, kind: 'class' }
case 'TypeAlias': {
const { id } = type as t.TypeAlias
const validatorId = this.getValidatorIdentifier(id)
const T = await this.importT()
const validatorId = this.getValidatorIdentifier(id)
const validatorIdWithType = this.getValidatorIdentifier(id)
validatorIdWithType.typeAnnotation = t.typeAnnotation(
t.genericTypeAnnotation(
t.qualifiedTypeIdentifier(t.identifier('Type'), T),
t.typeParameterInstantiation([t.genericTypeAnnotation(id)])
)
)
const validator: t.VariableDeclaration = templates.alias({
T,
ID: validatorId,
ID: validatorIdWithType,
NAME: t.stringLiteral(id.name),
TYPE: await this.convert(
(path as NodePath<t.TypeAlias>).get('right')
),
}) as any
;(validator.declarations[0]
.id as any).typeAnnotation = t.typeAnnotation(
t.genericTypeAnnotation(
t.qualifiedTypeIdentifier(t.identifier('Type'), T),
t.typeParameterInstantiation([t.genericTypeAnnotation(id)])
)
)

const { parentPath } = path
if (parentPath.isExportNamedDeclaration())
parentPath.insertAfter(t.exportNamedDeclaration(validator))
Expand All @@ -368,23 +402,24 @@ export class FileConversionContext {
}
case 'TSTypeAliasDeclaration': {
const { id } = type as t.TSTypeAliasDeclaration
const validatorId = this.getValidatorIdentifier(id)
const T = await this.importT()
const validatorId = this.getValidatorIdentifier(id)
const validatorIdWithType = this.getValidatorIdentifier(id)
validatorIdWithType.typeAnnotation = t.tsTypeAnnotation(
t.tsTypeReference(
t.tsQualifiedName(T, t.identifier('Type')),
t.tsTypeParameterInstantiation([t.tsTypeReference(id)])
)
)

const validator: t.VariableDeclaration = templates.alias({
T,
ID: validatorId,
ID: validatorIdWithType,
NAME: t.stringLiteral(id.name),
TYPE: await this.convert(
(path as NodePath<t.TSTypeAliasDeclaration>).get('typeAnnotation')
),
}) as any
;(validator.declarations[0]
.id as any).typeAnnotation = t.tsTypeAnnotation(
t.tsTypeReference(
t.tsQualifiedName(T, t.identifier('Type')),
t.tsTypeParameterInstantiation([t.tsTypeReference(id)])
)
)
const { parentPath } = path
if (parentPath.isExportNamedDeclaration())
parentPath.insertAfter(t.exportNamedDeclaration(validator))
Expand Down Expand Up @@ -470,10 +505,10 @@ export class FileConversionContext {
throw new NodeConversionError(`Unsupported type reference`, this.file, path)
}

async convertTypeReferenceToValidator(
path: NodePath<any>
): Promise<t.Expression> {
const { converted, kind } = await this.convertTypeReference(path)
async convertTypeReferenceToValidator({
converted,
kind,
}: ConvertedTypeReference): Promise<t.Expression> {
return kind === 'class'
? templates.instanceOf({ T: await this.importT(), CLASS: converted })
: kind === 'alias'
Expand All @@ -485,8 +520,9 @@ export class FileConversionContext {
const type = path.node
switch (type.type) {
case 'MixedTypeAnnotation':
case 'AnyTypeAnnotation':
case 'TSUnknownKeyword':
return templates.unknown({ T: await this.importT() })
case 'AnyTypeAnnotation':
case 'TSAnyKeyword':
return templates.any({ T: await this.importT() })
case 'VoidTypeAnnotation':
Expand Down Expand Up @@ -644,13 +680,17 @@ export class FileConversionContext {
const convertedUtility = await convertUtilityFlowType(this, path)
if (convertedUtility) return convertedUtility
return await this.convertTypeReferenceToValidator(
(path as NodePath<t.GenericTypeAnnotation>).get('id')
await this.convertTypeReference(
(path as NodePath<t.GenericTypeAnnotation>).get('id')
)
)
}
case 'TSTypeReference': {
if (isTSRecordType(path)) return await convertTSRecordType(this, path)
return await this.convertTypeReferenceToValidator(
(path as NodePath<t.TSTypeReference>).get('typeName')
await this.convertTypeReference(
(path as NodePath<t.TSTypeReference>).get('typeName')
)
)
}
}
Expand Down
19 changes: 19 additions & 0 deletions src/convert/moveCommentsToNextSibling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { NodePath } from '@babel/traverse'

export default function moveCommentsToNextSibling(path: NodePath<any>): void {
const nextSibling: NodePath<any> = path.getAllNextSiblings()[0]
if (!nextSibling) return
for (const key of [
'leadingComments',
'trailingComments',
'innerComments',
'comments',
]) {
const src = path.node[key]
if (!Array.isArray(src) || !src.length) continue
const dest = nextSibling.node[key] || (nextSibling.node[key] = [])
for (const comment of src) {
dest.unshift(comment)
}
}
}
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,13 @@
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==

"@types/babel__generator@^7.6.2":
version "7.6.2"
resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8"
integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==
dependencies:
"@babel/types" "^7.0.0"

"@types/babel__template@^7.4.0":
version "7.4.0"
resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz#0c888dd70b3ee9eebb6e4f200e809da0076262be"
Expand Down

0 comments on commit 9d6e02f

Please sign in to comment.