Skip to content

Commit

Permalink
Use NodeFactory to create and update nodes
Browse files Browse the repository at this point in the history
Most ts.create* and ts.update* functions are deprecated as of TypeScript 4.0. The
current plan is to emit noiser and noisier warnings with each version
until the deprecated functions are removed in 4.3.

See this pull request for more details:
microsoft/TypeScript#35282

Some method signatures and names have changed slightly.

The easy way to update is to use the global ts.factory object.
TypeScript transformations also have access to a NodeFactory in their
transformation contexts.

Note that we still use the deprecate getMutableClone function because
its replacement is not included in the TypeScript declarations. See
microsoft/TypeScript#40507
  • Loading branch information
jasonreyes9 committed Oct 24, 2020
1 parent 11a4379 commit e65a6ed
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 e65a6ed

Please sign in to comment.