Skip to content

Commit 2fcf263

Browse files
authored
Retain type annotation when transforming redundant types (#555)
1 parent e467f18 commit 2fcf263

File tree

7 files changed

+158
-105
lines changed

7 files changed

+158
-105
lines changed

.changeset/sweet-spies-attack.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"aws-sdk-js-codemod": patch
3+
---
4+
5+
Retain type annotation when transforming redundant types
Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,46 @@
11
const AWS = require("aws-sdk");
22

33
// Native types
4-
const stringType: AWS.S3.AccountId = "string";
5-
const booleanType: AWS.S3.BucketKeyEnabled = true;
6-
const numberType: AWS.S3.ContentLength = 123;
4+
const stringType: typeof AWS.S3.AccountId = "string";
5+
const booleanType: typeof AWS.S3.BucketKeyEnabled = true;
6+
const numberType: typeof AWS.S3.ContentLength = 123;
77

88
// Date
9-
const dateType: AWS.S3.CreationDate = new Date();
9+
const dateType: typeof AWS.S3.CreationDate = new Date();
1010

1111
// Uint8Array
12-
const blobType: AWS.RDSDataService._Blob = new Uint8Array();
12+
const blobType: typeof AWS.RDSDataService._Blob = new Uint8Array();
1313

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

2222
// Maps
23-
const stringMap: AWS.S3.Metadata = { key: "value" };
24-
const booleanMap: AWS.APIGateway.MapOfStringToBoolean = { key: true };
25-
const numberMap: AWS.SSM.AssociationStatusAggregatedCount = { key: 123 };
26-
const structureMap: AWS.APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };
23+
const stringMap: typeof AWS.S3.Metadata = { key: "value" };
24+
const booleanMap: typeof AWS.APIGateway.MapOfStringToBoolean = { key: true };
25+
const numberMap: typeof AWS.SSM.AssociationStatusAggregatedCount = { key: 123 };
26+
const structureMap: typeof AWS.APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };
2727

2828
// Nested arrays
29-
const arrayNestedTwice: AWS.SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
30-
const arrayNestedThrice: AWS.SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
31-
const arrayNestedFour: AWS.SageMakerGeospatial.LinearRingsList = [
29+
const arrayNestedTwice: typeof AWS.SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
30+
const arrayNestedThrice: typeof AWS.SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
31+
const arrayNestedFour: typeof AWS.SageMakerGeospatial.LinearRingsList = [
3232
[[[1], [2]], [[3], [4]]],
3333
[[[5], [6]], [[7], [8]]]
3434
];
3535

3636
// Nested maps
37-
const mapNestedTwice: AWS.LexModelsV2.ConditionMap = { key: stringMap };
38-
const mapNestedTwiceStruct: AWS.APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };
37+
const mapNestedTwice: typeof AWS.LexModelsV2.ConditionMap = { key: stringMap };
38+
const mapNestedTwiceStruct: typeof AWS.APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };
3939

4040
// Nested arrays and maps
41-
const mapOfArrays: AWS.NetworkManager.FilterMap = { key: ["value"] };
42-
const mapOfMapOfArrays: AWS.AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
43-
const mapOfArrayOfMaps: AWS.DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
44-
const mapOfArrayOfArrays: AWS.APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
45-
const arrayOfMaps: AWS.SSM.InventoryItemEntryList = [stringMap];
46-
const arrayOfMapOfArrays: AWS.SSM.TargetMaps = [mapOfArrays];
41+
const mapOfArrays: typeof AWS.NetworkManager.FilterMap = { key: ["value"] };
42+
const mapOfMapOfArrays: typeof AWS.AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
43+
const mapOfArrayOfMaps: typeof AWS.DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
44+
const mapOfArrayOfArrays: typeof AWS.APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
45+
const arrayOfMaps: typeof AWS.SSM.InventoryItemEntryList = [stringMap];
46+
const arrayOfMapOfArrays: typeof AWS.SSM.TargetMaps = [mapOfArrays];

src/transforms/v2-to-v3/__fixtures__/api-redundant-type/service-require-deep.input.ts

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,46 +10,46 @@ const AppIntegrations = require("aws-sdk/clients/appintegrations");
1010
const SSM = require("aws-sdk/clients/ssm");
1111

1212
// Native types
13-
const stringType: S3.AccountId = "string";
14-
const booleanType: S3.BucketKeyEnabled = true;
15-
const numberType: S3.ContentLength = 123;
13+
const stringType: typeof S3.AccountId = "string";
14+
const booleanType: typeof S3.BucketKeyEnabled = true;
15+
const numberType: typeof S3.ContentLength = 123;
1616

1717
// Date
18-
const dateType: S3.CreationDate = new Date();
18+
const dateType: typeof S3.CreationDate = new Date();
1919

2020
// Uint8Array
21-
const blobType: RDSDataService._Blob = new Uint8Array();
21+
const blobType: typeof RDSDataService._Blob = new Uint8Array();
2222

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

3131
// Maps
32-
const stringMap: S3.Metadata = { key: "value" };
33-
const booleanMap: APIGateway.MapOfStringToBoolean = { key: true };
34-
const numberMap: SSM.AssociationStatusAggregatedCount = { key: 123 };
35-
const structureMap: APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };
32+
const stringMap: typeof S3.Metadata = { key: "value" };
33+
const booleanMap: typeof APIGateway.MapOfStringToBoolean = { key: true };
34+
const numberMap: typeof SSM.AssociationStatusAggregatedCount = { key: 123 };
35+
const structureMap: typeof APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };
3636

3737
// Nested arrays
38-
const arrayNestedTwice: SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
39-
const arrayNestedThrice: SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
40-
const arrayNestedFour: SageMakerGeospatial.LinearRingsList = [
38+
const arrayNestedTwice: typeof SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
39+
const arrayNestedThrice: typeof SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
40+
const arrayNestedFour: typeof SageMakerGeospatial.LinearRingsList = [
4141
[[[1], [2]], [[3], [4]]],
4242
[[[5], [6]], [[7], [8]]]
4343
];
4444

4545
// Nested maps
46-
const mapNestedTwice: LexModelsV2.ConditionMap = { key: stringMap };
47-
const mapNestedTwiceStruct: APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };
46+
const mapNestedTwice: typeof LexModelsV2.ConditionMap = { key: stringMap };
47+
const mapNestedTwiceStruct: typeof APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };
4848

4949
// Nested arrays and maps
50-
const mapOfArrays: NetworkManager.FilterMap = { key: ["value"] };
51-
const mapOfMapOfArrays: AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
52-
const mapOfArrayOfMaps: DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
53-
const mapOfArrayOfArrays: APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
54-
const arrayOfMaps: SSM.InventoryItemEntryList = [stringMap];
55-
const arrayOfMapOfArrays: SSM.TargetMaps = [mapOfArrays];
50+
const mapOfArrays: typeof NetworkManager.FilterMap = { key: ["value"] };
51+
const mapOfMapOfArrays: typeof AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
52+
const mapOfArrayOfMaps: typeof DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
53+
const mapOfArrayOfArrays: typeof APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
54+
const arrayOfMaps: typeof SSM.InventoryItemEntryList = [stringMap];
55+
const arrayOfMapOfArrays: typeof SSM.TargetMaps = [mapOfArrays];

src/transforms/v2-to-v3/__fixtures__/api-redundant-type/service-require.input.ts

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,46 +10,46 @@ const { AppIntegrations } = require("aws-sdk");
1010
const { SSM } = require("aws-sdk");
1111

1212
// Native types
13-
const stringType: S3.AccountId = "string";
14-
const booleanType: S3.BucketKeyEnabled = true;
15-
const numberType: S3.ContentLength = 123;
13+
const stringType: typeof S3.AccountId = "string";
14+
const booleanType: typeof S3.BucketKeyEnabled = true;
15+
const numberType: typeof S3.ContentLength = 123;
1616

1717
// Date
18-
const dateType: S3.CreationDate = new Date();
18+
const dateType: typeof S3.CreationDate = new Date();
1919

2020
// Uint8Array
21-
const blobType: RDSDataService._Blob = new Uint8Array();
21+
const blobType: typeof RDSDataService._Blob = new Uint8Array();
2222

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

3131
// Maps
32-
const stringMap: S3.Metadata = { key: "value" };
33-
const booleanMap: APIGateway.MapOfStringToBoolean = { key: true };
34-
const numberMap: SSM.AssociationStatusAggregatedCount = { key: 123 };
35-
const structureMap: APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };
32+
const stringMap: typeof S3.Metadata = { key: "value" };
33+
const booleanMap: typeof APIGateway.MapOfStringToBoolean = { key: true };
34+
const numberMap: typeof SSM.AssociationStatusAggregatedCount = { key: 123 };
35+
const structureMap: typeof APIGateway.MapOfMethodSnapshot = { key: { apiKeyRequired: true } };
3636

3737
// Nested arrays
38-
const arrayNestedTwice: SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
39-
const arrayNestedThrice: SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
40-
const arrayNestedFour: SageMakerGeospatial.LinearRingsList = [
38+
const arrayNestedTwice: typeof SageMakerGeospatial.LinearRing = [[1, 2], [3, 4]];
39+
const arrayNestedThrice: typeof SageMakerGeospatial.LinearRings = [[[1, 2], [3, 4]], [[4, 5], [6, 7]]];
40+
const arrayNestedFour: typeof SageMakerGeospatial.LinearRingsList = [
4141
[[[1], [2]], [[3], [4]]],
4242
[[[5], [6]], [[7], [8]]]
4343
];
4444

4545
// Nested maps
46-
const mapNestedTwice: LexModelsV2.ConditionMap = { key: stringMap };
47-
const mapNestedTwiceStruct: APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };
46+
const mapNestedTwice: typeof LexModelsV2.ConditionMap = { key: stringMap };
47+
const mapNestedTwiceStruct: typeof APIGateway.PathToMapOfMethodSnapshot = { key: structureMap };
4848

4949
// Nested arrays and maps
50-
const mapOfArrays: NetworkManager.FilterMap = { key: ["value"] };
51-
const mapOfMapOfArrays: AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
52-
const mapOfArrayOfMaps: DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
53-
const mapOfArrayOfArrays: APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
54-
const arrayOfMaps: SSM.InventoryItemEntryList = [stringMap];
55-
const arrayOfMapOfArrays: SSM.TargetMaps = [mapOfArrays];
50+
const mapOfArrays: typeof NetworkManager.FilterMap = { key: ["value"] };
51+
const mapOfMapOfArrays: typeof AppIntegrations.ObjectConfiguration = { key: mapOfArrays };
52+
const mapOfArrayOfMaps: typeof DynamoDB.BatchGetResponseMap = { key: [{ key: { S:"A" }}] };
53+
const mapOfArrayOfArrays: typeof APIGateway.MapOfKeyUsages = { key: [[1], [2]] };
54+
const arrayOfMaps: typeof SSM.InventoryItemEntryList = [stringMap];
55+
const arrayOfMapOfArrays: typeof SSM.TargetMaps = [mapOfArrays];
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { TSQualifiedName } from "jscodeshift";
2+
3+
export const getTSQualifiedNameFromClientName = (
4+
v2GlobalName: string,
5+
clientName: string
6+
): TSQualifiedName => {
7+
// Support for DynamoDB.DocumentClient
8+
const [clientNamePrefix, clientNameSuffix] = clientName.split(".");
9+
10+
if (clientNameSuffix) {
11+
return {
12+
left: {
13+
left: { type: "Identifier", name: v2GlobalName },
14+
right: { type: "Identifier", name: clientNamePrefix },
15+
},
16+
right: { type: "Identifier", name: clientNameSuffix },
17+
} as TSQualifiedName;
18+
}
19+
20+
return {
21+
left: { type: "Identifier", name: v2GlobalName },
22+
right: { type: "Identifier", name: clientNamePrefix },
23+
} as TSQualifiedName;
24+
};

src/transforms/v2-to-v3/ts-type/replaceTSQualifiedName.ts

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { ASTPath, Collection, Identifier, JSCodeshift, TSQualifiedName } from "j
22

33
import { DOCUMENT_CLIENT, DYNAMODB, DYNAMODB_DOCUMENT_CLIENT } from "../config";
44
import { getClientTypeNames } from "./getClientTypeNames";
5+
import { getTSQualifiedNameFromClientName } from "./getTSQualifiedNameFromClientName";
56
import { getV3ClientTypeReference } from "./getV3ClientTypeReference";
7+
import { updateV2ClientTypeRef } from "./updateV2ClientTypeRef";
68

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

21-
const getTSQualifiedNameFromClientName = (
22-
v2GlobalName: string,
23-
clientName: string
24-
): TSQualifiedName => {
25-
// Support for DynamoDB.DocumentClient
26-
const [clientNamePrefix, clientNameSuffix] = clientName.split(".");
27-
28-
if (clientNameSuffix) {
29-
return {
30-
left: {
31-
left: { type: "Identifier", name: v2GlobalName },
32-
right: { type: "Identifier", name: clientNamePrefix },
33-
},
34-
right: { type: "Identifier", name: clientNameSuffix },
35-
} as TSQualifiedName;
36-
}
37-
38-
return {
39-
left: { type: "Identifier", name: v2GlobalName },
40-
right: { type: "Identifier", name: clientNamePrefix },
41-
} as TSQualifiedName;
42-
};
43-
4423
// Replace v2 client type reference with v3 client type reference.
4524
export const replaceTSQualifiedName = (
4625
j: JSCodeshift,
@@ -65,9 +44,13 @@ export const replaceTSQualifiedName = (
6544
(v2ClientType) =>
6645
isRightSectionIdentifier(v2ClientType.node) && !isParentTSQualifiedName(v2ClientType)
6746
)
68-
.replaceWith((v2ClientType) => {
47+
.forEach((v2ClientType) => {
6948
const v2ClientTypeName = getRightIdentifierName(v2ClientType.node);
70-
return getV3ClientTypeReference(j, { v2ClientName, v2ClientTypeName, v2ClientLocalName });
49+
updateV2ClientTypeRef(j, v2ClientType, {
50+
v2ClientName,
51+
v2ClientTypeName,
52+
v2ClientLocalName,
53+
});
7154
});
7255
}
7356

@@ -88,9 +71,13 @@ export const replaceTSQualifiedName = (
8871
(v2ClientType) =>
8972
isRightSectionIdentifier(v2ClientType.node) && !isParentTSQualifiedName(v2ClientType)
9073
)
91-
.replaceWith((v2ClientType) => {
74+
.forEach((v2ClientType) => {
9275
const v2ClientTypeName = getRightIdentifierName(v2ClientType.node);
93-
return getV3ClientTypeReference(j, { v2ClientName, v2ClientTypeName, v2ClientLocalName });
76+
updateV2ClientTypeRef(j, v2ClientType, {
77+
v2ClientName,
78+
v2ClientTypeName,
79+
v2ClientLocalName,
80+
});
9481
});
9582

9683
// Replace type reference to client type with modules.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { ASTPath, Identifier, JSCodeshift, TSQualifiedName, TSTypeReference } from "jscodeshift";
2+
import { getV3ClientTypeReference } from "./getV3ClientTypeReference";
3+
4+
const nativeTsRefTypes = ["TSAnyKeyword", "TSStringKeyword", "TSNumberKeyword", "TSBooleanKeyword"];
5+
const nativeTsIdentifierTypes = ["Date", "Uint8Array", "Array", "Record"];
6+
7+
interface UpdateV2ClientTypeRefOptions {
8+
v2ClientName: string;
9+
v2ClientTypeName: string;
10+
v2ClientLocalName: string;
11+
}
12+
13+
export const updateV2ClientTypeRef = (
14+
j: JSCodeshift,
15+
v2ClientType: ASTPath<TSQualifiedName>,
16+
{ v2ClientName, v2ClientTypeName, v2ClientLocalName }: UpdateV2ClientTypeRefOptions
17+
) => {
18+
const v3ClientTypeRef = getV3ClientTypeReference(j, {
19+
v2ClientName,
20+
v2ClientTypeName,
21+
v2ClientLocalName,
22+
});
23+
24+
if (
25+
(v2ClientType.parentPath?.value.type === "TSTypeQuery" &&
26+
nativeTsRefTypes.includes(v3ClientTypeRef.type)) ||
27+
(v3ClientTypeRef.type === "TSTypeReference" &&
28+
(v3ClientTypeRef as TSTypeReference).typeName.type === "Identifier" &&
29+
nativeTsIdentifierTypes.includes(
30+
((v3ClientTypeRef as TSTypeReference).typeName as Identifier).name
31+
))
32+
) {
33+
v2ClientType.parentPath?.replace(v3ClientTypeRef);
34+
} else {
35+
v2ClientType.replace(v3ClientTypeRef);
36+
}
37+
};

0 commit comments

Comments
 (0)