Created
December 5, 2017 04:31
-
-
Save qix/393f57cce9994185e2136d50eabcc733 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const ts = require('typescript'); | |
const prettier = require('prettier'); | |
const any = () => ts.createTypeReferenceNode('any', []); | |
function replaceParameters(parameters) { | |
let changed = false; | |
const rv = parameters.map(param => { | |
changed = changed || (!param.questionToken && !param.initializer) || !param.type; | |
let type; | |
if (param.dotDotDotToken) { | |
type = param.type || ts.createTypeReferenceNode('Array', [any()]); | |
} else { | |
type = param.type || any(); | |
} | |
let questionToken = param.questionToken; | |
if (!questionToken && !param.initializer && !param.dotDotDotToken) { | |
questionToken = ts.createToken(ts.SyntaxKind.QuestionToken); | |
} | |
return ts.updateParameter( | |
param, | |
param.decorators, | |
param.modifiers, | |
param.dotDotDotToken, | |
param.name, | |
questionToken, | |
type, | |
param.initializer | |
); | |
}); | |
return changed ? rv : parameters; | |
} | |
function returnType(node) { | |
const isAsync = | |
node.modifiers && | |
node.modifiers.some(dec => { | |
return dec.kind === ts.SyntaxKind.AsyncKeyword; | |
}); | |
if (isAsync) { | |
return node.type || ts.createTypeReferenceNode('Promise', [any()]); | |
} else { | |
return node.type || any(); | |
} | |
} | |
function applyAnyTransform(ast, tscodeshift) { | |
const transform = node => { | |
const rv = applyAnyTransform(tscodeshift(node), tscodeshift); | |
return rv.rootNode; | |
}; | |
ast.find(ts.SyntaxKind.ClassDeclaration).replaceWith(node => { | |
let members = [...node.members]; | |
if (members[0].kind !== ts.SyntaxKind.IndexSignature) { | |
members.unshift( | |
ts.createIndexSignature( | |
undefined, | |
undefined, | |
[ | |
ts.createParameter( | |
[], | |
[], | |
undefined, | |
ts.createIdentifier('x'), | |
undefined, | |
ts.createTypeReferenceNode('string', []), | |
undefined | |
), | |
], | |
any() | |
) | |
); | |
} | |
return ts.updateClassDeclaration( | |
node, | |
node.decorators, | |
node.modifiers, | |
node.name, | |
node.typeParameters, | |
node.heritageClauses, | |
members | |
); | |
}); | |
ast.find(ts.SyntaxKind.FunctionDeclaration).replaceWith(node => { | |
const parameters = replaceParameters(node.parameters); | |
return ts.updateFunctionDeclaration( | |
node, | |
node.decorators, | |
node.modifiers, | |
node.asteriskToken, | |
node.name, | |
node.typeParameters, | |
node.parameters && replaceParameters(node.parameters), | |
returnType(node), | |
transform(node.body) | |
); | |
}); | |
ast.find(ts.SyntaxKind.ArrowFunction).replaceWith(node => { | |
const parameters = replaceParameters(node.parameters); | |
return ts.updateArrowFunction( | |
node, | |
node.modifiers, | |
node.typeParameters, | |
replaceParameters(node.parameters), | |
returnType(node), | |
transform(node.body) | |
); | |
}); | |
ast.find(ts.SyntaxKind.MethodDeclaration).replaceWith(node => { | |
const parameters = replaceParameters(node.parameters); | |
return ts.updateMethod( | |
node, | |
node.decorators, | |
node.modifiers, | |
node.asteriskToken, | |
node.name, | |
node.questionToken, | |
node.typeParameters, | |
replaceParameters(node.parameters), | |
returnType(node), | |
transform(node.body) | |
); | |
}); | |
ast.find(ts.SyntaxKind.Constructor).replaceWith(node => { | |
return ts.updateConstructor( | |
node, | |
node.decorators, | |
node.modifiers, | |
replaceParameters(node.parameters), | |
transform(node.body) | |
); | |
}); | |
// `module.exports =` => `export =` | |
ast.find(ts.SyntaxKind.BinaryExpression).replaceWith(node => { | |
if (node.operatorToken.kind !== ts.SyntaxKind.EqualsToken) { | |
return node; | |
} | |
if ( | |
node.left.kind === ts.SyntaxKind.PropertyAccessExpression && | |
node.left.expression.kind === ts.SyntaxKind.Identifier && | |
node.left.expression.escapedText === 'module' && | |
node.left.name.escapedText === 'exports' | |
) { | |
return ts.createBinary(ts.createIdentifier('export'), node.operatorToken, node.right); | |
} | |
return node; | |
}); | |
ast.find(ts.SyntaxKind.VariableStatement).replaceWith(node => { | |
if (node.declarationList.declarations.length > 1) { | |
return node; | |
} | |
const decl = node.declarationList.declarations[0]; | |
const { initializer } = decl; | |
if ( | |
initializer && | |
initializer.kind === ts.SyntaxKind.CallExpression && | |
initializer.expression.kind === ts.SyntaxKind.Identifier && | |
initializer.expression.escapedText === 'require' && | |
initializer.arguments.length === 1 | |
) { | |
return ts.createImportEqualsDeclaration([], [], decl.name, initializer); | |
} | |
return node; | |
}); | |
ast.find(ts.SyntaxKind.VariableDeclaration).replaceWith(node => { | |
if ( | |
node.parent && | |
node.parent.parent && | |
(node.parent.parent.kind === ts.SyntaxKind.ForOfStatement || | |
node.parent.parent.kind === ts.SyntaxKind.ForInStatement) | |
) { | |
// Don't transform LHS of `for of` | |
type = node.type; | |
} else { | |
type = node.type || any(); | |
} | |
changed = true; | |
return ts.updateVariableDeclaration(node, node.name, type, node.initializer && transform(node.initializer)); | |
}); | |
return ast; | |
} | |
function updateParams(file, api) { | |
const { tscodeshift } = api; | |
let ast = tscodeshift(file.source); | |
ast = applyAnyTransform(ast, tscodeshift); | |
return ast.toSource(); | |
} | |
module.exports = { | |
default: updateParams, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment