Skip to content

Commit

Permalink
Merge pull request #56 from edsrzf/ts-factory
Browse files Browse the repository at this point in the history
Use NodeFactory to create and update nodes
  • Loading branch information
Rudeg authored Oct 26, 2020
2 parents 2bd81a1 + de0e51c commit 8c1f8c1
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 185 deletions.
8 changes: 4 additions & 4 deletions packages/ts-migrate-example/src/example-plugin-ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,24 @@ const examplePluginTs: Plugin<Options> = {

if (options.shouldReplaceText && hasTwoParams && multiplierReturn) {
// create a new function declaration with a new type
const newFunctionDeclaration = ts.createFunctionDeclaration(
const newFunctionDeclaration = ts.factory.createFunctionDeclaration(
functionDeclaration.decorators,
functionDeclaration.modifiers,
functionDeclaration.asteriskToken,
functionDeclaration.name,
functionDeclaration.typeParameters,
functionDeclaration.parameters.map((x) =>
ts.createParameter(
ts.factory.createParameterDeclaration(
x.decorators,
x.modifiers,
x.dotDotDotToken,
x.name,
x.questionToken,
ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
x.initializer,
),
),
ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
functionDeclaration.body,
);

Expand Down
23 changes: 11 additions & 12 deletions packages/ts-migrate-plugins/src/plugins/hoist-class-statics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ type Options = {

const hoistClassStaticsPlugin: Plugin<Options> = {
name: 'hoist-class-statics',
run({ fileName, text, options }) {
return hoistStaticClassProperties(fileName, text, options);
run({ sourceFile, text, options }) {
return hoistStaticClassProperties(sourceFile, text, options);
},
};

Expand Down Expand Up @@ -90,11 +90,10 @@ function isAlreadyHoisted(
}

function hoistStaticClassProperties(
fileName: string,
sourceFile: ts.SourceFile,
sourceText: string,
options: Options,
): string {
const sourceFile = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest, true);
const printer = ts.createPrinter();
const updates: SourceTextUpdate[] = [];

Expand Down Expand Up @@ -126,9 +125,9 @@ function hoistStaticClassProperties(
canHoistExpression(statement.expression.right, classDeclaration.pos, knownDefinitions)
) {
properties.push(
ts.createProperty(
ts.factory.createPropertyDeclaration(
undefined,
[ts.createModifier(ts.SyntaxKind.StaticKeyword)],
[ts.factory.createModifier(ts.SyntaxKind.StaticKeyword)],
statement.expression.left.name.text,
undefined,
undefined,
Expand All @@ -143,14 +142,14 @@ function hoistStaticClassProperties(
} else {
// otherwise add a static type annotation for this expression
properties.push(
ts.createProperty(
ts.factory.createPropertyDeclaration(
undefined,
[ts.createModifier(ts.SyntaxKind.StaticKeyword)],
[ts.factory.createModifier(ts.SyntaxKind.StaticKeyword)],
statement.expression.left.name.text,
undefined,
options.anyAlias != null
? ts.createTypeReferenceNode(options.anyAlias, undefined)
: ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
? ts.factory.createTypeReferenceNode(options.anyAlias, undefined)
: ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
undefined,
),
);
Expand All @@ -160,14 +159,14 @@ function hoistStaticClassProperties(

if (properties.length > 0) {
if (classDeclaration.members.length === 0) {
const updatedClassDeclaration = ts.updateClassDeclaration(
const updatedClassDeclaration = ts.factory.updateClassDeclaration(
classDeclaration,
classDeclaration.decorators,
classDeclaration.modifiers,
classDeclaration.name,
classDeclaration.typeParameters,
classDeclaration.heritageClauses,
ts.createNodeArray(properties),
ts.factory.createNodeArray(properties),
);

let index = classDeclaration.pos;
Expand Down
62 changes: 35 additions & 27 deletions packages/ts-migrate-plugins/src/plugins/jsdoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ const jsDocTransformerFactory = ({
anyAlias,
typeMap: optionsTypeMap,
}: Options) => (context: ts.TransformationContext) => {
const { factory } = context;
const anyType = anyAlias
? ts.createTypeReferenceNode(anyAlias, undefined)
: ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
? factory.createTypeReferenceNode(anyAlias, undefined)
: factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
const typeMap: TypeMap = { ...defaultTypeMap, ...optionsTypeMap };

function visit<T extends ts.Node>(origNode: T): T {
Expand All @@ -78,7 +79,9 @@ const jsDocTransformerFactory = ({

function visitFunctionLike<T extends ts.SignatureDeclaration>(node: T, insideClass: boolean): T {
const modifiers =
ts.isMethodDeclaration(node) && insideClass ? modifiersFromJSDoc(node) : node.modifiers;
ts.isMethodDeclaration(node) && insideClass
? modifiersFromJSDoc(node, factory)
: node.modifiers;
const parameters = visitParameters(node);
const returnType = annotateReturns ? visitReturnType(node) : node.type;
if (
Expand All @@ -90,8 +93,8 @@ const jsDocTransformerFactory = ({
}

const newNode = ts.getMutableClone(node) as any;
newNode.modifiers = ts.createNodeArray(modifiers);
newNode.parameters = ts.createNodeArray(parameters);
newNode.modifiers = factory.createNodeArray(modifiers);
newNode.parameters = factory.createNodeArray(parameters);
newNode.type = returnType;
return newNode;
}
Expand Down Expand Up @@ -121,10 +124,10 @@ const jsDocTransformerFactory = ({
!param.initializer &&
ts.isIdentifier(param.name) &&
(paramNode.isBracketed || ts.isJSDocOptionalType(typeNode))
? ts.createToken(ts.SyntaxKind.QuestionToken)
? factory.createToken(ts.SyntaxKind.QuestionToken)
: param.questionToken;

const newParam = ts.createParameter(
const newParam = factory.createParameterDeclaration(
param.decorators,
param.modifiers,
param.dotDotDotToken,
Expand Down Expand Up @@ -192,25 +195,25 @@ const jsDocTransformerFactory = ({
}

function visitJSDocOptionalType(node: ts.JSDocOptionalType) {
return ts.createUnionTypeNode([
return factory.createUnionTypeNode([
ts.visitNode(node.type, visitJSDocType),
ts.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
]);
}

function visitJSDocNullableType(node: ts.JSDocNullableType) {
return ts.createUnionTypeNode([
return factory.createUnionTypeNode([
ts.visitNode(node.type, visitJSDocType),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword as any),
factory.createKeywordTypeNode(ts.SyntaxKind.NullKeyword as any),
]);
}

function visitJSDocVariadicType(node: ts.JSDocVariadicType) {
return ts.createArrayTypeNode(ts.visitNode(node.type, visitJSDocType));
return factory.createArrayTypeNode(ts.visitNode(node.type, visitJSDocType));
}

function visitJSDocFunctionType(node: ts.JSDocFunctionType) {
return ts.createFunctionTypeNode(
return factory.createFunctionTypeNode(
undefined,
node.parameters.map(visitJSDocParameter),
node.type ?? anyType,
Expand All @@ -227,7 +230,7 @@ const jsDocTransformerFactory = ({
}
});
}
return ts.createTypeLiteralNode(propertySignatures);
return factory.createTypeLiteralNode(propertySignatures);
}

function visitJSDocPropertyLikeTag(node: ts.JSDocPropertyLikeTag) {
Expand All @@ -240,12 +243,14 @@ const jsDocTransformerFactory = ({
type = anyType;
}
const questionToken =
node.isBracketed || optionalType ? ts.createToken(ts.SyntaxKind.QuestionToken) : undefined;
node.isBracketed || optionalType
? factory.createToken(ts.SyntaxKind.QuestionToken)
: undefined;
if (ts.isIdentifier(node.name)) {
return ts.createPropertySignature(undefined, node.name, questionToken, type, undefined);
return factory.createPropertySignature(undefined, node.name, questionToken, type);
}
// Assumption: the leaf field on the QualifiedName belongs directly to the parent object type.
return ts.createPropertySignature(undefined, node.name.right, questionToken, type, undefined);
return factory.createPropertySignature(undefined, node.name.right, questionToken, type);
}

function visitJSDocParameter(node: ts.ParameterDeclaration) {
Expand All @@ -257,8 +262,10 @@ const jsDocTransformerFactory = ({
node.type.kind === ts.SyntaxKind.JSDocVariadicType &&
index === node.parent.parameters.length - 1;
const name = node.name || (isRest ? 'rest' : `arg${index}`);
const dotdotdot = isRest ? ts.createToken(ts.SyntaxKind.DotDotDotToken) : node.dotDotDotToken;
return ts.createParameter(
const dotdotdot = isRest
? factory.createToken(ts.SyntaxKind.DotDotDotToken)
: node.dotDotDotToken;
return factory.createParameterDeclaration(
node.decorators,
node.modifiers,
dotdotdot,
Expand Down Expand Up @@ -290,35 +297,35 @@ const jsDocTransformerFactory = ({
}
}

name = ts.createIdentifier(text);
name = factory.createIdentifier(text);
if ((text === 'Array' || text === 'Promise') && !node.typeArguments) {
args = ts.createNodeArray([anyType]);
args = factory.createNodeArray([anyType]);
} else if (acceptsTypeParameters) {
args = ts.visitNodes(node.typeArguments, visitJSDocType);
}
if (!acceptsTypeParameters) {
args = undefined;
}
}
return ts.createTypeReferenceNode(name, args);
return factory.createTypeReferenceNode(name, args);
}

function visitJSDocIndexSignature(node: ts.TypeReferenceNode) {
const typeArguments = node.typeArguments!;
const index = ts.createParameter(
const index = factory.createParameterDeclaration(
/* decorators */ undefined,
/* modifiers */ undefined,
/* dotDotDotToken */ undefined,
typeArguments[0].kind === ts.SyntaxKind.NumberKeyword ? 'n' : 's',
/* questionToken */ undefined,
ts.createTypeReferenceNode(
factory.createTypeReferenceNode(
typeArguments[0].kind === ts.SyntaxKind.NumberKeyword ? 'number' : 'string',
[],
),
/* initializer */ undefined,
);
const indexSignature = ts.createTypeLiteralNode([
ts.createIndexSignature(
const indexSignature = factory.createTypeLiteralNode([
factory.createIndexSignature(
/* decorators */ undefined,
/* modifiers */ undefined,
[index],
Expand All @@ -337,6 +344,7 @@ const accessibilityMask =

function modifiersFromJSDoc(
methodDeclaration: ts.MethodDeclaration,
factory: ts.NodeFactory,
): ReadonlyArray<ts.Modifier> | undefined {
let modifierFlags = ts.getCombinedModifierFlags(methodDeclaration);
if ((modifierFlags & accessibilityMask) !== 0) {
Expand All @@ -354,7 +362,7 @@ function modifiersFromJSDoc(
return methodDeclaration.modifiers;
}

return ts.createModifiersFromModifierFlags(modifierFlags);
return factory.createModifiersFromModifierFlags(modifierFlags);
}

// Copied from: https://github.com/microsoft/TypeScript/blob/v4.0.2/src/compiler/utilities.ts#L1879
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const accessibilityMask =
const memberAccessibilityTransformerFactory = (options: Options) => (
context: ts.TransformationContext,
) => {
const { factory } = context;
let defaultAccessibility: ts.ModifierFlags;
switch (options.defaultAccessibility) {
case 'private':
Expand Down Expand Up @@ -77,8 +78,8 @@ const memberAccessibilityTransformerFactory = (options: Options) => (
}

const newNode = ts.getMutableClone(node) as any;
newNode.modifiers = ts.createNodeArray(
ts.createModifiersFromModifierFlags(modifierFlags | accessibilityFlag),
newNode.modifiers = factory.createNodeArray(
factory.createModifiersFromModifierFlags(modifierFlags | accessibilityFlag),
);
return newNode;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ type Options = { force?: boolean };

const reactClassLifecycleMethodsPlugin: Plugin<Options> = {
name: 'react-class-lifecycle-methods',
run({ fileName, text, options }) {
run({ fileName, sourceFile, text, options }) {
return /\.tsx$/.test(fileName)
? annotateReactComponentLifecycleMethods(fileName, text, options.force)
? annotateReactComponentLifecycleMethods(sourceFile, text, options.force)
: undefined;
},
};
Expand Down Expand Up @@ -37,7 +37,7 @@ const reactLifecycleMethodAnnotations: { [method: string]: AnnotationKind[] } =
};

function updateParameterType(parameter: ts.ParameterDeclaration, type: ts.TypeNode | undefined) {
return ts.updateParameter(
return ts.factory.updateParameterDeclaration(
parameter,
parameter.decorators,
parameter.modifiers,
Expand All @@ -50,24 +50,25 @@ function updateParameterType(parameter: ts.ParameterDeclaration, type: ts.TypeNo
}

function annotateReactComponentLifecycleMethods(
fileName: string,
sourceFile: ts.SourceFile,
sourceText: string,
force = false,
) {
const sourceFile = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest, true);
const printer = ts.createPrinter();
const updates: SourceTextUpdate[] = [];

sourceFile.statements.forEach((statement) => {
if (ts.isClassDeclaration(statement) && isReactClassComponent(statement)) {
const heritageType = getReactComponentHeritageType(statement)!;
const heritageTypeArgs = heritageType.typeArguments || [];
const propsType = heritageTypeArgs[0] || ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
const stateType = heritageTypeArgs[1] || ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
const propsType =
heritageTypeArgs[0] || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
const stateType =
heritageTypeArgs[1] || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
const annotationToType = {
[AnnotationKind.Props]: propsType,
[AnnotationKind.State]: stateType,
[AnnotationKind.Context]: ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
[AnnotationKind.Context]: ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
};

statement.members.forEach((member) => {
Expand Down Expand Up @@ -110,7 +111,7 @@ function annotateReactComponentLifecycleMethods(

let text = printer.printList(
ts.ListFormat.Parameters,
ts.createNodeArray(parametersToPrint),
ts.factory.createNodeArray(parametersToPrint),
sourceFile,
);
// Remove surrounding parentheses
Expand Down
18 changes: 7 additions & 11 deletions packages/ts-migrate-plugins/src/plugins/react-class-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ const reactClassStatePlugin: Plugin<Options> = {
const stateTypeName = getStateTypeName();
const anyType =
options.anyAlias != null
? ts.createTypeReferenceNode(options.anyAlias, undefined)
: ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
const newStateType = ts.createTypeAliasDeclaration(
? ts.factory.createTypeReferenceNode(options.anyAlias, undefined)
: ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
const newStateType = ts.factory.createTypeAliasDeclaration(
undefined,
undefined,
stateTypeName,
Expand All @@ -81,14 +81,10 @@ const reactClassStatePlugin: Plugin<Options> = {
length: heritageType.end - heritageType.pos,
text: ` ${printer.printNode(
ts.EmitHint.Unspecified,
ts.updateExpressionWithTypeArguments(
heritageType,
[
propsType || ts.createTypeLiteralNode([]),
ts.createTypeReferenceNode(stateTypeName, undefined),
],
heritageType.expression,
),
ts.factory.updateExpressionWithTypeArguments(heritageType, heritageType.expression, [
propsType || ts.factory.createTypeLiteralNode([]),
ts.factory.createTypeReferenceNode(stateTypeName, undefined),
]),
sourceFile,
)}`,
});
Expand Down
Loading

0 comments on commit 8c1f8c1

Please sign in to comment.