Skip to content

Commit

Permalink
Retain type annotation when transforming redundant types (#555)
Browse files Browse the repository at this point in the history
  • Loading branch information
trivikr authored Aug 8, 2023
1 parent e467f18 commit 2fcf263
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 105 deletions.
5 changes: 5 additions & 0 deletions .changeset/sweet-spies-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"aws-sdk-js-codemod": patch
---

Retain type annotation when transforming redundant types
Original file line number Diff line number Diff line change
@@ -1,46 +1,46 @@
const AWS = require("aws-sdk");

// Native types
const stringType: AWS.S3.AccountId = "string";
const booleanType: AWS.S3.BucketKeyEnabled = true;
const numberType: AWS.S3.ContentLength = 123;
const stringType: typeof AWS.S3.AccountId = "string";
const booleanType: typeof AWS.S3.BucketKeyEnabled = true;
const numberType: typeof AWS.S3.ContentLength = 123;

// Date
const dateType: AWS.S3.CreationDate = new Date();
const dateType: typeof AWS.S3.CreationDate = new Date();

// Uint8Array
const blobType: AWS.RDSDataService._Blob = new Uint8Array();
const blobType: typeof AWS.RDSDataService._Blob = new Uint8Array();

// Arrays
const stringArray: AWS.S3.AllowedHeaders = ["string1", "string2"];
const booleanArray: AWS.RDSDataService.BooleanArray = [true, false];
const numberArray: AWS.RDSDataService.LongArray = [123, 456];
const blobArray: AWS.IoTFleetWise.NetworkFilesList = [new Uint8Array()];
const enumArray: AWS.S3.ChecksumAlgorithmList = ["CRC32"];
const structureArray: AWS.S3.Buckets = [{ Name: "bucketName" }];
const stringArray: typeof AWS.S3.AllowedHeaders = ["string1", "string2"];
const booleanArray: typeof AWS.RDSDataService.BooleanArray = [true, false];
const numberArray: typeof AWS.RDSDataService.LongArray = [123, 456];
const blobArray: typeof AWS.IoTFleetWise.NetworkFilesList = [new Uint8Array()];
const enumArray: typeof AWS.S3.ChecksumAlgorithmList = ["CRC32"];
const structureArray: typeof AWS.S3.Buckets = [{ Name: "bucketName" }];

// Maps
const stringMap: AWS.S3.Metadata = { key: "value" };
const booleanMap: AWS.APIGateway.MapOfStringToBoolean = { key: true };
const numberMap: AWS.SSM.AssociationStatusAggregatedCount = { key: 123 };
const structureMap: AWS.APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };
const stringMap: typeof AWS.S3.Metadata = { key: "value" };
const booleanMap: typeof AWS.APIGateway.MapOfStringToBoolean = { key: true };
const numberMap: typeof AWS.SSM.AssociationStatusAggregatedCount = { key: 123 };
const structureMap: typeof AWS.APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };

// Nested arrays
const arrayNestedTwice: AWS.SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
const arrayNestedThrice: AWS.SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
const arrayNestedFour: AWS.SageMakerGeospatial.LinearRingsList = [
const arrayNestedTwice: typeof AWS.SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
const arrayNestedThrice: typeof AWS.SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
const arrayNestedFour: typeof AWS.SageMakerGeospatial.LinearRingsList = [
[[[1], [2]], [[3], [4]]],
[[[5], [6]], [[7], [8]]]
];

// Nested maps
const mapNestedTwice: AWS.LexModelsV2.ConditionMap = { key: stringMap };
const mapNestedTwiceStruct: AWS.APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };
const mapNestedTwice: typeof AWS.LexModelsV2.ConditionMap = { key: stringMap };
const mapNestedTwiceStruct: typeof AWS.APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };

// Nested arrays and maps
const mapOfArrays: AWS.NetworkManager.FilterMap = { key: ["value"] };
const mapOfMapOfArrays: AWS.AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
const mapOfArrayOfMaps: AWS.DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
const mapOfArrayOfArrays: AWS.APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
const arrayOfMaps: AWS.SSM.InventoryItemEntryList = [stringMap];
const arrayOfMapOfArrays: AWS.SSM.TargetMaps = [mapOfArrays];
const mapOfArrays: typeof AWS.NetworkManager.FilterMap = { key: ["value"] };
const mapOfMapOfArrays: typeof AWS.AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
const mapOfArrayOfMaps: typeof AWS.DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
const mapOfArrayOfArrays: typeof AWS.APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
const arrayOfMaps: typeof AWS.SSM.InventoryItemEntryList = [stringMap];
const arrayOfMapOfArrays: typeof AWS.SSM.TargetMaps = [mapOfArrays];
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,46 @@ const AppIntegrations = require("aws-sdk/clients/appintegrations");
const SSM = require("aws-sdk/clients/ssm");

// Native types
const stringType: S3.AccountId = "string";
const booleanType: S3.BucketKeyEnabled = true;
const numberType: S3.ContentLength = 123;
const stringType: typeof S3.AccountId = "string";
const booleanType: typeof S3.BucketKeyEnabled = true;
const numberType: typeof S3.ContentLength = 123;

// Date
const dateType: S3.CreationDate = new Date();
const dateType: typeof S3.CreationDate = new Date();

// Uint8Array
const blobType: RDSDataService._Blob = new Uint8Array();
const blobType: typeof RDSDataService._Blob = new Uint8Array();

// Arrays
const stringArray: S3.AllowedHeaders = ["string1", "string2"];
const booleanArray: RDSDataService.BooleanArray = [true, false];
const numberArray: RDSDataService.LongArray = [123, 456];
const blobArray: IoTFleetWise.NetworkFilesList = [new Uint8Array()];
const enumArray: S3.ChecksumAlgorithmList = ["CRC32"];
const structureArray: S3.Buckets = [{ Name: "bucketName" }];
const stringArray: typeof S3.AllowedHeaders = ["string1", "string2"];
const booleanArray: typeof RDSDataService.BooleanArray = [true, false];
const numberArray: typeof RDSDataService.LongArray = [123, 456];
const blobArray: typeof IoTFleetWise.NetworkFilesList = [new Uint8Array()];
const enumArray: typeof S3.ChecksumAlgorithmList = ["CRC32"];
const structureArray: typeof S3.Buckets = [{ Name: "bucketName" }];

// Maps
const stringMap: S3.Metadata = { key: "value" };
const booleanMap: APIGateway.MapOfStringToBoolean = { key: true };
const numberMap: SSM.AssociationStatusAggregatedCount = { key: 123 };
const structureMap: APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };
const stringMap: typeof S3.Metadata = { key: "value" };
const booleanMap: typeof APIGateway.MapOfStringToBoolean = { key: true };
const numberMap: typeof SSM.AssociationStatusAggregatedCount = { key: 123 };
const structureMap: typeof APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };

// Nested arrays
const arrayNestedTwice: SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
const arrayNestedThrice: SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
const arrayNestedFour: SageMakerGeospatial.LinearRingsList = [
const arrayNestedTwice: typeof SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
const arrayNestedThrice: typeof SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
const arrayNestedFour: typeof SageMakerGeospatial.LinearRingsList = [
[[[1], [2]], [[3], [4]]],
[[[5], [6]], [[7], [8]]]
];

// Nested maps
const mapNestedTwice: LexModelsV2.ConditionMap = { key: stringMap };
const mapNestedTwiceStruct: APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };
const mapNestedTwice: typeof LexModelsV2.ConditionMap = { key: stringMap };
const mapNestedTwiceStruct: typeof APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };

// Nested arrays and maps
const mapOfArrays: NetworkManager.FilterMap = { key: ["value"] };
const mapOfMapOfArrays: AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
const mapOfArrayOfMaps: DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
const mapOfArrayOfArrays: APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
const arrayOfMaps: SSM.InventoryItemEntryList = [stringMap];
const arrayOfMapOfArrays: SSM.TargetMaps = [mapOfArrays];
const mapOfArrays: typeof NetworkManager.FilterMap = { key: ["value"] };
const mapOfMapOfArrays: typeof AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
const mapOfArrayOfMaps: typeof DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
const mapOfArrayOfArrays: typeof APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
const arrayOfMaps: typeof SSM.InventoryItemEntryList = [stringMap];
const arrayOfMapOfArrays: typeof SSM.TargetMaps = [mapOfArrays];
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,46 @@ const { AppIntegrations } = require("aws-sdk");
const { SSM } = require("aws-sdk");

// Native types
const stringType: S3.AccountId = "string";
const booleanType: S3.BucketKeyEnabled = true;
const numberType: S3.ContentLength = 123;
const stringType: typeof S3.AccountId = "string";
const booleanType: typeof S3.BucketKeyEnabled = true;
const numberType: typeof S3.ContentLength = 123;

// Date
const dateType: S3.CreationDate = new Date();
const dateType: typeof S3.CreationDate = new Date();

// Uint8Array
const blobType: RDSDataService._Blob = new Uint8Array();
const blobType: typeof RDSDataService._Blob = new Uint8Array();

// Arrays
const stringArray: S3.AllowedHeaders = ["string1", "string2"];
const booleanArray: RDSDataService.BooleanArray = [true, false];
const numberArray: RDSDataService.LongArray = [123, 456];
const blobArray: IoTFleetWise.NetworkFilesList = [new Uint8Array()];
const enumArray: S3.ChecksumAlgorithmList = ["CRC32"];
const structureArray: S3.Buckets = [{ Name: "bucketName" }];
const stringArray: typeof S3.AllowedHeaders = ["string1", "string2"];
const booleanArray: typeof RDSDataService.BooleanArray = [true, false];
const numberArray: typeof RDSDataService.LongArray = [123, 456];
const blobArray: typeof IoTFleetWise.NetworkFilesList = [new Uint8Array()];
const enumArray: typeof S3.ChecksumAlgorithmList = ["CRC32"];
const structureArray: typeof S3.Buckets = [{ Name: "bucketName" }];

// Maps
const stringMap: S3.Metadata = { key: "value" };
const booleanMap: APIGateway.MapOfStringToBoolean = { key: true };
const numberMap: SSM.AssociationStatusAggregatedCount = { key: 123 };
const structureMap: APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };
const stringMap: typeof S3.Metadata = { key: "value" };
const booleanMap: typeof APIGateway.MapOfStringToBoolean = { key: true };
const numberMap: typeof SSM.AssociationStatusAggregatedCount = { key: 123 };
const structureMap: typeof APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };

// Nested arrays
const arrayNestedTwice: SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
const arrayNestedThrice: SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
const arrayNestedFour: SageMakerGeospatial.LinearRingsList = [
const arrayNestedTwice: typeof SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
const arrayNestedThrice: typeof SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
const arrayNestedFour: typeof SageMakerGeospatial.LinearRingsList = [
[[[1], [2]], [[3], [4]]],
[[[5], [6]], [[7], [8]]]
];

// Nested maps
const mapNestedTwice: LexModelsV2.ConditionMap = { key: stringMap };
const mapNestedTwiceStruct: APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };
const mapNestedTwice: typeof LexModelsV2.ConditionMap = { key: stringMap };
const mapNestedTwiceStruct: typeof APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };

// Nested arrays and maps
const mapOfArrays: NetworkManager.FilterMap = { key: ["value"] };
const mapOfMapOfArrays: AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
const mapOfArrayOfMaps: DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
const mapOfArrayOfArrays: APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
const arrayOfMaps: SSM.InventoryItemEntryList = [stringMap];
const arrayOfMapOfArrays: SSM.TargetMaps = [mapOfArrays];
const mapOfArrays: typeof NetworkManager.FilterMap = { key: ["value"] };
const mapOfMapOfArrays: typeof AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
const mapOfArrayOfMaps: typeof DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
const mapOfArrayOfArrays: typeof APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
const arrayOfMaps: typeof SSM.InventoryItemEntryList = [stringMap];
const arrayOfMapOfArrays: typeof SSM.TargetMaps = [mapOfArrays];
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { TSQualifiedName } from "jscodeshift";

export const getTSQualifiedNameFromClientName = (
v2GlobalName: string,
clientName: string
): TSQualifiedName => {
// Support for DynamoDB.DocumentClient
const [clientNamePrefix, clientNameSuffix] = clientName.split(".");

if (clientNameSuffix) {
return {
left: {
left: { type: "Identifier", name: v2GlobalName },
right: { type: "Identifier", name: clientNamePrefix },
},
right: { type: "Identifier", name: clientNameSuffix },
} as TSQualifiedName;
}

return {
left: { type: "Identifier", name: v2GlobalName },
right: { type: "Identifier", name: clientNamePrefix },
} as TSQualifiedName;
};
41 changes: 14 additions & 27 deletions src/transforms/v2-to-v3/ts-type/replaceTSQualifiedName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { ASTPath, Collection, Identifier, JSCodeshift, TSQualifiedName } from "j

import { DOCUMENT_CLIENT, DYNAMODB, DYNAMODB_DOCUMENT_CLIENT } from "../config";
import { getClientTypeNames } from "./getClientTypeNames";
import { getTSQualifiedNameFromClientName } from "./getTSQualifiedNameFromClientName";
import { getV3ClientTypeReference } from "./getV3ClientTypeReference";
import { updateV2ClientTypeRef } from "./updateV2ClientTypeRef";

export interface ReplaceTSQualifiedNameOptions {
v2ClientName: string;
Expand All @@ -18,29 +20,6 @@ const getRightIdentifierName = (node: TSQualifiedName) => (node.right as Identif
const isParentTSQualifiedName = (node: ASTPath<TSQualifiedName>) =>
node.parentPath?.value.type === "TSQualifiedName";

const getTSQualifiedNameFromClientName = (
v2GlobalName: string,
clientName: string
): TSQualifiedName => {
// Support for DynamoDB.DocumentClient
const [clientNamePrefix, clientNameSuffix] = clientName.split(".");

if (clientNameSuffix) {
return {
left: {
left: { type: "Identifier", name: v2GlobalName },
right: { type: "Identifier", name: clientNamePrefix },
},
right: { type: "Identifier", name: clientNameSuffix },
} as TSQualifiedName;
}

return {
left: { type: "Identifier", name: v2GlobalName },
right: { type: "Identifier", name: clientNamePrefix },
} as TSQualifiedName;
};

// Replace v2 client type reference with v3 client type reference.
export const replaceTSQualifiedName = (
j: JSCodeshift,
Expand All @@ -65,9 +44,13 @@ export const replaceTSQualifiedName = (
(v2ClientType) =>
isRightSectionIdentifier(v2ClientType.node) && !isParentTSQualifiedName(v2ClientType)
)
.replaceWith((v2ClientType) => {
.forEach((v2ClientType) => {
const v2ClientTypeName = getRightIdentifierName(v2ClientType.node);
return getV3ClientTypeReference(j, { v2ClientName, v2ClientTypeName, v2ClientLocalName });
updateV2ClientTypeRef(j, v2ClientType, {
v2ClientName,
v2ClientTypeName,
v2ClientLocalName,
});
});
}

Expand All @@ -88,9 +71,13 @@ export const replaceTSQualifiedName = (
(v2ClientType) =>
isRightSectionIdentifier(v2ClientType.node) && !isParentTSQualifiedName(v2ClientType)
)
.replaceWith((v2ClientType) => {
.forEach((v2ClientType) => {
const v2ClientTypeName = getRightIdentifierName(v2ClientType.node);
return getV3ClientTypeReference(j, { v2ClientName, v2ClientTypeName, v2ClientLocalName });
updateV2ClientTypeRef(j, v2ClientType, {
v2ClientName,
v2ClientTypeName,
v2ClientLocalName,
});
});

// Replace type reference to client type with modules.
Expand Down
37 changes: 37 additions & 0 deletions src/transforms/v2-to-v3/ts-type/updateV2ClientTypeRef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ASTPath, Identifier, JSCodeshift, TSQualifiedName, TSTypeReference } from "jscodeshift";
import { getV3ClientTypeReference } from "./getV3ClientTypeReference";

const nativeTsRefTypes = ["TSAnyKeyword", "TSStringKeyword", "TSNumberKeyword", "TSBooleanKeyword"];
const nativeTsIdentifierTypes = ["Date", "Uint8Array", "Array", "Record"];

interface UpdateV2ClientTypeRefOptions {
v2ClientName: string;
v2ClientTypeName: string;
v2ClientLocalName: string;
}

export const updateV2ClientTypeRef = (
j: JSCodeshift,
v2ClientType: ASTPath<TSQualifiedName>,
{ v2ClientName, v2ClientTypeName, v2ClientLocalName }: UpdateV2ClientTypeRefOptions
) => {
const v3ClientTypeRef = getV3ClientTypeReference(j, {
v2ClientName,
v2ClientTypeName,
v2ClientLocalName,
});

if (
(v2ClientType.parentPath?.value.type === "TSTypeQuery" &&
nativeTsRefTypes.includes(v3ClientTypeRef.type)) ||
(v3ClientTypeRef.type === "TSTypeReference" &&
(v3ClientTypeRef as TSTypeReference).typeName.type === "Identifier" &&
nativeTsIdentifierTypes.includes(
((v3ClientTypeRef as TSTypeReference).typeName as Identifier).name
))
) {
v2ClientType.parentPath?.replace(v3ClientTypeRef);
} else {
v2ClientType.replace(v3ClientTypeRef);
}
};

0 comments on commit 2fcf263

Please sign in to comment.