Skip to content

Instantly share code, notes, and snippets.

@qix
Created December 5, 2017 04:31
Show Gist options
  • Save qix/393f57cce9994185e2136d50eabcc733 to your computer and use it in GitHub Desktop.
Save qix/393f57cce9994185e2136d50eabcc733 to your computer and use it in GitHub Desktop.
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