Skip to content

Commit

Permalink
Support transformation for global require (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
trivikr authored Mar 8, 2022
1 parent 12c2e5a commit 65c166a
Show file tree
Hide file tree
Showing 15 changed files with 205 additions and 43 deletions.
5 changes: 5 additions & 0 deletions .changeset/lemon-mangos-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"aws-sdk-js-codemod": minor
---

Support transformation for global require
3 changes: 3 additions & 0 deletions src/transforms/v2-to-v3/__fixtures__/global-require.input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const AWS = require("aws-sdk");

const client = new AWS.DynamoDB();
5 changes: 5 additions & 0 deletions src/transforms/v2-to-v3/__fixtures__/global-require.output.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const {
DynamoDB
} = require("@aws-sdk/client-dynamodb");

const client = new DynamoDB();
8 changes: 4 additions & 4 deletions src/transforms/v2-to-v3/transformer.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { API, FileInfo } from "jscodeshift";

import {
addV3ClientImport,
addV3ClientModule,
getClientMetadata,
getV2ClientImportNames,
getV2ClientNames,
getV2DefaultImportName,
removeDefaultImportIfNotUsed,
removeDefaultModuleIfNotUsed,
removePromiseCalls,
removeV2ClientImport,
replaceClientCreation,
Expand All @@ -27,13 +27,13 @@ export default function transformer(file: FileInfo, api: API) {

for (const [v2ClientName, v3ClientMetadata] of Object.entries(clientMetadata).reverse()) {
const { v3ClientName, v3ClientPackageName } = v3ClientMetadata;
addV3ClientImport(j, source, { v2ClientName, v3ClientName, v3ClientPackageName });
addV3ClientModule(j, source, { v2ClientName, v3ClientName, v3ClientPackageName });
removeV2ClientImport(j, source, v2ClientName);
removePromiseCalls(j, source, { v2DefaultImportName, v2ClientName });
replaceClientCreation(j, source, { v2DefaultImportName, v2ClientName, v3ClientName });
}

removeDefaultImportIfNotUsed(j, source, v2DefaultImportName);
removeDefaultModuleIfNotUsed(j, source, v2DefaultImportName);

return source.toSource();
}
8 changes: 2 additions & 6 deletions src/transforms/v2-to-v3/utils/addV3ClientImport.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { Collection, JSCodeshift } from "jscodeshift";

export interface AddV3ClientImportOptions {
v2ClientName: string;
v3ClientName: string;
v3ClientPackageName: string;
}
import { AddV3ClientModuleOptions } from "./addV3ClientModule";

export const addV3ClientImport = (
j: JSCodeshift,
source: Collection<any>,
{ v2ClientName, v3ClientName, v3ClientPackageName }: AddV3ClientImportOptions
{ v2ClientName, v3ClientName, v3ClientPackageName }: AddV3ClientModuleOptions
): void => {
const existingImports = source.find(j.ImportDeclaration, {
source: { value: v3ClientPackageName },
Expand Down
20 changes: 20 additions & 0 deletions src/transforms/v2-to-v3/utils/addV3ClientModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Collection, JSCodeshift } from "jscodeshift";

import { addV3ClientImport } from "./addV3ClientImport";
import { addV3ClientRequire } from "./addV3ClientRequire";
import { containsRequire } from "./containsRequire";

export interface AddV3ClientModuleOptions {
v2ClientName: string;
v3ClientName: string;
v3ClientPackageName: string;
}

export const addV3ClientModule = (
j: JSCodeshift,
source: Collection<any>,
{ v2ClientName, v3ClientName, v3ClientPackageName }: AddV3ClientModuleOptions
): void =>
containsRequire(j, source)
? addV3ClientRequire(j, source, { v2ClientName, v3ClientName, v3ClientPackageName })
: addV3ClientImport(j, source, { v2ClientName, v3ClientName, v3ClientPackageName });
60 changes: 60 additions & 0 deletions src/transforms/v2-to-v3/utils/addV3ClientRequire.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Collection, Identifier, JSCodeshift, ObjectPattern, Property } from "jscodeshift";

import { AddV3ClientModuleOptions } from "./addV3ClientModule";
import { getRequireVariableDeclaration } from "./getRequireVariableDeclaration";

export const addV3ClientRequire = (
j: JSCodeshift,
source: Collection<any>,
{ v2ClientName, v3ClientName, v3ClientPackageName }: AddV3ClientModuleOptions
): void => {
const v3ClientNameIdentifier = j.identifier(v3ClientName);
const v3ClientNameProperty = j.property.from({
kind: "init",
key: v3ClientNameIdentifier,
shorthand: true,
value: v3ClientNameIdentifier,
});

const existingRequires = source.find(j.VariableDeclarator, {
id: { type: "ObjectPattern" },
init: {
type: "CallExpression",
callee: { type: "Identifier", name: "require" },
arguments: [{ type: "Literal", value: v3ClientPackageName }],
},
});

// Require decleration already exists.
if (existingRequires.size()) {
existingRequires.forEach((nodePath) => {
// Append to existing require if property not present.
if (
!(nodePath.value.id as ObjectPattern).properties.find(
(property) => ((property as Property).key as Identifier).name === v3ClientName
)
) {
(nodePath.value.id as ObjectPattern).properties.push(v3ClientNameProperty);
}
});
return;
}

// Insert after default require if present. If not, insert after client require.
const clientRequireValue = `aws-sdk/clients/${v2ClientName.toLowerCase()}`;
const defaultRequireVarDeclaration = getRequireVariableDeclaration(j, source, "aws-sdk");
const clientRequireVarDeclaration = getRequireVariableDeclaration(j, source, clientRequireValue);

const requireVarDeclaration =
defaultRequireVarDeclaration.size() > 0
? defaultRequireVarDeclaration
: clientRequireVarDeclaration;
requireVarDeclaration.insertAfter(
j.variableDeclaration("const", [
j.variableDeclarator(
j.objectPattern([v3ClientNameProperty]),
j.callExpression(j.identifier("require"), [j.literal(v3ClientPackageName)])
),
])
);
};
8 changes: 8 additions & 0 deletions src/transforms/v2-to-v3/utils/containsRequire.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Collection, JSCodeshift } from "jscodeshift";

export const containsRequire = (j: JSCodeshift, source: Collection<any>) =>
source
.find(j.CallExpression, {
callee: { type: "Identifier", name: "require" },
})
.size() > 0;
18 changes: 18 additions & 0 deletions src/transforms/v2-to-v3/utils/getRequireVariableDeclaration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Collection, JSCodeshift } from "jscodeshift";

export const getRequireVariableDeclaration = (
j: JSCodeshift,
source: Collection<any>,
literalValue: string
) =>
source.find(j.VariableDeclaration, {
declarations: [
{
init: {
type: "CallExpression",
callee: { type: "Identifier", name: "require" },
arguments: [{ type: "Literal", value: literalValue }],
},
},
],
});
16 changes: 15 additions & 1 deletion src/transforms/v2-to-v3/utils/getV2DefaultImportName.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Collection, JSCodeshift } from "jscodeshift";
import { Collection, Identifier, JSCodeshift } from "jscodeshift";

export const getV2DefaultImportName = (
j: JSCodeshift,
Expand All @@ -22,5 +22,19 @@ export const getV2DefaultImportName = (
});
});

// Set specifier name in v2DefaultImportName if it is required in the source.
source
.find(j.VariableDeclarator, {
id: { type: "Identifier" },
init: {
type: "CallExpression",
callee: { type: "Identifier", name: "require" },
arguments: [{ type: "Literal", value: "aws-sdk" }],
},
})
.forEach((declerationPath) => {
v2DefaultImportName = (declerationPath.value.id as Identifier).name;
});

return v2DefaultImportName;
};
5 changes: 3 additions & 2 deletions src/transforms/v2-to-v3/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export * from "./addV3ClientImport";
export * from "./addV3ClientModule";
export * from "./containsRequire";
export * from "./getClientMetadata";
export * from "./getV2ClientImportNames";
export * from "./getV2ClientNames";
export * from "./getV2DefaultImportName";
export * from "./removeDefaultImportIfNotUsed";
export * from "./removeDefaultModuleIfNotUsed";
export * from "./removePromiseCalls";
export * from "./removeV2ClientImport";
export * from "./replaceClientCreation";
Expand Down
23 changes: 23 additions & 0 deletions src/transforms/v2-to-v3/utils/removeDefaultImport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Collection, JSCodeshift } from "jscodeshift";

export const removeDefaultImport = (
j: JSCodeshift,
source: Collection<any>,
defaultImportName: string
) => {
source
.find(j.ImportDeclaration, {
specifiers: [{ local: { name: defaultImportName } }],
source: { value: "aws-sdk" },
})
.forEach((declerationPath) => {
// Remove default import from ImportDeclaration.
declerationPath.value.specifiers = declerationPath.value.specifiers.filter(
(specifier) => specifier.local.name !== defaultImportName
);
// Remove ImportDeclaration if there are no other imports.
if (declerationPath.value.specifiers.length === 0) {
j(declerationPath).remove();
}
});
};
30 changes: 0 additions & 30 deletions src/transforms/v2-to-v3/utils/removeDefaultImportIfNotUsed.ts

This file was deleted.

22 changes: 22 additions & 0 deletions src/transforms/v2-to-v3/utils/removeDefaultModuleIfNotUsed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Collection, JSCodeshift } from "jscodeshift";

import { containsRequire } from "./containsRequire";
import { removeDefaultImport } from "./removeDefaultImport";
import { removeDefaultRequire } from "./removeDefaultRequire";

export const removeDefaultModuleIfNotUsed = (
j: JSCodeshift,
source: Collection<any>,
defaultImportName: string
) => {
const identifierUsages = source.find(j.Identifier, { name: defaultImportName });

// Only usage is import/require.
if (identifierUsages.size() === 1) {
if (containsRequire(j, source)) {
removeDefaultRequire(j, source, defaultImportName);
} else {
removeDefaultImport(j, source, defaultImportName);
}
}
};
17 changes: 17 additions & 0 deletions src/transforms/v2-to-v3/utils/removeDefaultRequire.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Collection, Identifier, JSCodeshift, VariableDeclarator } from "jscodeshift";

import { getRequireVariableDeclaration } from "./getRequireVariableDeclaration";

export const removeDefaultRequire = (
j: JSCodeshift,
source: Collection<any>,
defaultImportName: string
) => {
getRequireVariableDeclaration(j, source, "aws-sdk")
.filter(
(nodePath) =>
((nodePath.value.declarations[0] as VariableDeclarator).id as Identifier).name ===
defaultImportName
)
.remove();
};

0 comments on commit 65c166a

Please sign in to comment.