diff --git a/.changeset/brave-hounds-do.md b/.changeset/brave-hounds-do.md new file mode 100644 index 000000000..8baf8c41c --- /dev/null +++ b/.changeset/brave-hounds-do.md @@ -0,0 +1,5 @@ +--- +"aws-sdk-js-codemod": patch +--- + +Add utility requireModule getImportSpecifiers diff --git a/src/transforms/v2-to-v3/apis/getNodesWithDocClientNamedImportFromDeepPath.ts b/src/transforms/v2-to-v3/apis/getNodesWithDocClientNamedImportFromDeepPath.ts index d2ee70c5c..0482e3647 100644 --- a/src/transforms/v2-to-v3/apis/getNodesWithDocClientNamedImportFromDeepPath.ts +++ b/src/transforms/v2-to-v3/apis/getNodesWithDocClientNamedImportFromDeepPath.ts @@ -7,6 +7,7 @@ import { } from "jscodeshift"; import { DOCUMENT_CLIENT, DYNAMODB, OBJECT_PROPERTY_TYPE_LIST } from "../config"; import { ImportType } from "../modules"; +import { getRequireDeclarators } from "../modules/requireModule"; import { getClientDeepImportPath } from "../utils"; export const getNodesWithDocClientNamedImportFromDeepPath = ( @@ -17,14 +18,7 @@ export const getNodesWithDocClientNamedImportFromDeepPath = ( const deepImportPath = getClientDeepImportPath(DYNAMODB); if (importType === ImportType.REQUIRE) { - return source - .find(j.VariableDeclarator, { - init: { - type: "CallExpression", - callee: { type: "Identifier", name: "require" }, - arguments: [{ value: deepImportPath }], - }, - }) + return getRequireDeclarators(j, source, deepImportPath) .filter( (variableDeclarator) => variableDeclarator.value.id.type === "ObjectPattern" && diff --git a/src/transforms/v2-to-v3/client-names/getClientNamesRecordFromRequire.ts b/src/transforms/v2-to-v3/client-names/getClientNamesRecordFromRequire.ts index fb7dc45c1..3c8089ffb 100644 --- a/src/transforms/v2-to-v3/client-names/getClientNamesRecordFromRequire.ts +++ b/src/transforms/v2-to-v3/client-names/getClientNamesRecordFromRequire.ts @@ -1,16 +1,9 @@ -import { - Collection, - Identifier, - JSCodeshift, - ObjectPattern, - ObjectProperty, - Property, -} from "jscodeshift"; +import { Collection, JSCodeshift } from "jscodeshift"; -import { CLIENT_NAMES, OBJECT_PROPERTY_TYPE_LIST, PACKAGE_NAME } from "../config"; -import { getRequireDeclaratorsWithProperty } from "../modules"; +import { CLIENT_NAMES, PACKAGE_NAME } from "../config"; +import { ImportSpecifierDefault, ImportSpecifierPattern } from "../modules"; +import { getImportSpecifiers } from "../modules/requireModule"; import { getClientDeepImportPath } from "../utils"; -import { getRequireIds } from "./getRequireIds"; export const getClientNamesRecordFromRequire = ( j: JSCodeshift, @@ -19,51 +12,23 @@ export const getClientNamesRecordFromRequire = ( ) => { const clientNamesRecord: Record = {}; - const idPropertiesFromObjectPattern = getRequireIds(j, source, PACKAGE_NAME) - .filter((id) => id.type === "ObjectPattern") - .map((objectPattern) => (objectPattern as ObjectPattern).properties) - .flat(); + const idPropertiesFromObjectPattern = getImportSpecifiers(j, source, PACKAGE_NAME).filter( + (importSpecifier) => typeof importSpecifier === "object" + ) as ImportSpecifierPattern[]; - for (const idProperty of idPropertiesFromObjectPattern) { - if (!OBJECT_PROPERTY_TYPE_LIST.includes(idProperty.type)) { - continue; - } - const key = (idProperty as Property | ObjectProperty).key; - const value = (idProperty as Property | ObjectProperty).value; - if (key.type !== "Identifier" || value.type !== "Identifier") { - continue; - } - if (CLIENT_NAMES.includes(key.name)) { - clientNamesRecord[key.name] = value.name; - } - } - - const declaratorsWithProperty = getRequireDeclaratorsWithProperty(j, source, { - sourceValue: PACKAGE_NAME, - }).nodes(); - - for (const declaratorWithProperty of declaratorsWithProperty) { - const { id, init } = declaratorWithProperty; - if ( - id.type === "Identifier" && - init != undefined && - init.type === "MemberExpression" && - init.property.type === "Identifier" - ) { - const clientName = (init.property as Identifier).name; - if (CLIENT_NAMES.includes(clientName)) { - clientNamesRecord[clientName] = (id as Identifier).name; - } + for (const { importedName, localName } of idPropertiesFromObjectPattern) { + if (CLIENT_NAMES.includes(importedName)) { + clientNamesRecord[importedName] = localName || importedName; } } for (const clientName of clientNamesFromDeepImport) { const deepImportPath = getClientDeepImportPath(clientName); - const idsFromDefaultImport = getRequireIds(j, source, deepImportPath).filter( - (id) => id.type === "Identifier" - ); + const idsFromDefaultImport = getImportSpecifiers(j, source, deepImportPath).filter( + (importSpecifier) => typeof importSpecifier === "string" + ) as ImportSpecifierDefault[]; if (idsFromDefaultImport.length) { - clientNamesRecord[clientName] = (idsFromDefaultImport[0] as Identifier).name; + clientNamesRecord[clientName] = idsFromDefaultImport[0]; } } diff --git a/src/transforms/v2-to-v3/client-names/getRequireIds.ts b/src/transforms/v2-to-v3/client-names/getRequireIds.ts deleted file mode 100644 index 8b4344269..000000000 --- a/src/transforms/v2-to-v3/client-names/getRequireIds.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Collection, JSCodeshift } from "jscodeshift"; - -export const getRequireIds = (j: JSCodeshift, source: Collection, sourceValue: string) => - source - .find(j.VariableDeclarator, { - init: { - type: "CallExpression", - callee: { type: "Identifier", name: "require" }, - arguments: [{ value: sourceValue }], - }, - }) - .nodes() - .map((variableDeclarator) => variableDeclarator.id); diff --git a/src/transforms/v2-to-v3/modules/getGlobalNameFromModule.ts b/src/transforms/v2-to-v3/modules/getGlobalNameFromModule.ts index 6bb2b9307..4cae5b234 100644 --- a/src/transforms/v2-to-v3/modules/getGlobalNameFromModule.ts +++ b/src/transforms/v2-to-v3/modules/getGlobalNameFromModule.ts @@ -3,20 +3,14 @@ import { Collection, Identifier, JSCodeshift } from "jscodeshift"; import { PACKAGE_NAME } from "../config"; import { getImportEqualsDeclarationType } from "./getImportEqualsDeclarationType"; import { getImportSpecifiers } from "./getImportSpecifiers"; +import { getRequireDeclarators } from "./requireModule"; export const getGlobalNameFromModule = ( j: JSCodeshift, source: Collection ): string | undefined => { - const requireIdentifiers = source - .find(j.VariableDeclarator, { - id: { type: "Identifier" }, - init: { - type: "CallExpression", - callee: { type: "Identifier", name: "require" }, - arguments: [{ value: PACKAGE_NAME }], - }, - }) + const requireIdentifiers = getRequireDeclarators(j, source, PACKAGE_NAME) + .filter((declarator) => declarator.value.id.type === "Identifier") .nodes(); if (requireIdentifiers.length > 0) { diff --git a/src/transforms/v2-to-v3/modules/getImportSpecifier.ts b/src/transforms/v2-to-v3/modules/getImportSpecifier.ts deleted file mode 100644 index f136f6dc9..000000000 --- a/src/transforms/v2-to-v3/modules/getImportSpecifier.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { JSCodeshift } from "jscodeshift"; - -import { ImportSpecifierOptions } from "./types"; - -export const getImportSpecifier = ( - j: JSCodeshift, - { importedName, localName }: ImportSpecifierOptions -) => - localName - ? j.importSpecifier(j.identifier(importedName), j.identifier(localName)) - : j.importSpecifier(j.identifier(importedName)); diff --git a/src/transforms/v2-to-v3/modules/getRequireDeclarators.ts b/src/transforms/v2-to-v3/modules/getRequireDeclarators.ts deleted file mode 100644 index 12da511af..000000000 --- a/src/transforms/v2-to-v3/modules/getRequireDeclarators.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Collection, JSCodeshift } from "jscodeshift"; - -export const getRequireDeclarators = ( - j: JSCodeshift, - source: Collection, - sourceValue: string -) => - source.find(j.VariableDeclarator, { - init: { - arguments: [{ value: sourceValue }], - callee: { type: "Identifier", name: "require" }, - type: "CallExpression", - }, - type: "VariableDeclarator", - }); diff --git a/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithIdentifier.ts b/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithIdentifier.ts index 240740d6c..2edb10535 100644 --- a/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithIdentifier.ts +++ b/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithIdentifier.ts @@ -1,6 +1,5 @@ import { Collection, JSCodeshift } from "jscodeshift"; - -import { getRequireDeclarators } from "./getRequireDeclarators"; +import { getRequireDeclarators } from "./requireModule"; export interface GetRequireDeclaratorsWithIdentifier { identifierName: string; diff --git a/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithObjectPattern.ts b/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithObjectPattern.ts index 0eac500a8..25784025a 100644 --- a/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithObjectPattern.ts +++ b/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithObjectPattern.ts @@ -1,7 +1,7 @@ import { Collection, JSCodeshift, ObjectProperty, Property } from "jscodeshift"; import { OBJECT_PROPERTY_TYPE_LIST } from "../config"; -import { getRequireDeclarators } from "./getRequireDeclarators"; +import { getRequireDeclarators } from "./requireModule"; export interface GetRequireDeclaratorsWithObjectPattern { identifierName: string; diff --git a/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithProperty.ts b/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithProperty.ts index 2be5ffea9..513c0f0c1 100644 --- a/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithProperty.ts +++ b/src/transforms/v2-to-v3/modules/getRequireDeclaratorsWithProperty.ts @@ -1,4 +1,5 @@ -import { Collection, JSCodeshift } from "jscodeshift"; +import { Collection, Identifier, JSCodeshift } from "jscodeshift"; +import { getRequireDeclarators } from "./requireModule"; export interface GetRequireDeclaratorsWithPropertyOptions { localName?: string; @@ -11,15 +12,19 @@ export const getRequireDeclaratorsWithProperty = ( source: Collection, { localName, identifierName, sourceValue }: GetRequireDeclaratorsWithPropertyOptions ) => - source.find(j.VariableDeclarator, { - id: { type: "Identifier", ...(localName && { name: localName }) }, - init: { - type: "MemberExpression", - object: { - type: "CallExpression", - callee: { type: "Identifier", name: "require" }, - arguments: [{ value: sourceValue }], - }, - property: { type: "Identifier", ...(identifierName && { name: identifierName }) }, - }, + getRequireDeclarators(j, source, sourceValue).filter((varDeclarator) => { + const declaratorId = varDeclarator.value.id; + const declaratorInit = varDeclarator.value.init; + + if (declaratorId.type === "Identifier") { + const declaratorIdName = declaratorId.name; + if (declaratorInit!.type === "MemberExpression") { + const importedName = (declaratorInit.property as Identifier).name; + if (localName === declaratorIdName && identifierName === importedName) { + return true; + } + } + } + + return false; }); diff --git a/src/transforms/v2-to-v3/modules/getRequireProperty.ts b/src/transforms/v2-to-v3/modules/getRequireProperty.ts index e3f0640ce..c0ed1c801 100644 --- a/src/transforms/v2-to-v3/modules/getRequireProperty.ts +++ b/src/transforms/v2-to-v3/modules/getRequireProperty.ts @@ -1,10 +1,10 @@ import { JSCodeshift } from "jscodeshift"; -import { ImportSpecifierOptions } from "./types"; +import { ImportSpecifierPattern } from "./types"; export const getRequireProperty = ( j: JSCodeshift, - { importedName, localName }: ImportSpecifierOptions + { importedName, localName }: ImportSpecifierPattern ) => j.objectProperty.from({ key: j.identifier(importedName), diff --git a/src/transforms/v2-to-v3/modules/hasRequire.ts b/src/transforms/v2-to-v3/modules/hasRequire.ts index a16af972b..e7455e7f9 100644 --- a/src/transforms/v2-to-v3/modules/hasRequire.ts +++ b/src/transforms/v2-to-v3/modules/hasRequire.ts @@ -1,13 +1,5 @@ -import { Collection, JSCodeshift, Literal } from "jscodeshift"; -import { PACKAGE_NAME } from "../config"; +import { Collection, JSCodeshift } from "jscodeshift"; +import { getRequireDeclarators } from "./requireModule"; export const hasRequire = (j: JSCodeshift, source: Collection) => - source - .find(j.CallExpression, { - callee: { type: "Identifier", name: "require" }, - }) - .filter((callExpression) => { - const { value: sourceValue } = callExpression.value.arguments[0] as Literal; - return typeof sourceValue === "string" && sourceValue.startsWith(PACKAGE_NAME); - }) - .size() > 0; + getRequireDeclarators(j, source).size() > 0; diff --git a/src/transforms/v2-to-v3/modules/importModule/addNamedModule.ts b/src/transforms/v2-to-v3/modules/importModule/addNamedModule.ts index 7d021bea5..1d5079e94 100644 --- a/src/transforms/v2-to-v3/modules/importModule/addNamedModule.ts +++ b/src/transforms/v2-to-v3/modules/importModule/addNamedModule.ts @@ -8,7 +8,6 @@ import { } from "jscodeshift"; import { OBJECT_PROPERTY_TYPE_LIST, PACKAGE_NAME } from "../../config"; -import { getImportSpecifier } from "../getImportSpecifier"; import { getImportSpecifiers } from "../getImportSpecifiers"; import { getRequireProperty } from "../getRequireProperty"; import { importSpecifierCompareFn } from "../importSpecifierCompareFn"; @@ -101,7 +100,9 @@ export const addNamedModule = ( // Add named import to the first import declaration. const firstImportDeclSpecifiers = importDeclarations.nodes()[0].specifiers; if (firstImportDeclSpecifiers) { - firstImportDeclSpecifiers.push(getImportSpecifier(j, { importedName, localName })); + firstImportDeclSpecifiers.push( + j.importSpecifier(j.identifier(importedName), j.identifier(localName)) + ); firstImportDeclSpecifiers.sort(importSpecifierCompareFn); return; } @@ -109,7 +110,7 @@ export const addNamedModule = ( // Build a new import declaration. const v3ImportDeclaration = j.importDeclaration( - [getImportSpecifier(j, { importedName, localName })], + [j.importSpecifier(j.identifier(importedName), j.identifier(localName))], j.stringLiteral(packageName) ); diff --git a/src/transforms/v2-to-v3/modules/removeRequireIdentifier.ts b/src/transforms/v2-to-v3/modules/removeRequireIdentifier.ts index a40513167..ab4f4a155 100644 --- a/src/transforms/v2-to-v3/modules/removeRequireIdentifier.ts +++ b/src/transforms/v2-to-v3/modules/removeRequireIdentifier.ts @@ -9,6 +9,8 @@ export interface RemoveRequireIdentifierOptions { sourceValue: string; } +// ToDo: Write generic utility to remove unused modules +// Similar to https://github.com/aws/aws-sdk-js-codemod/pull/781 export const removeRequireIdentifier = ( j: JSCodeshift, source: Collection, diff --git a/src/transforms/v2-to-v3/modules/removeRequireProperty.ts b/src/transforms/v2-to-v3/modules/removeRequireProperty.ts index 05aab8626..8393d2119 100644 --- a/src/transforms/v2-to-v3/modules/removeRequireProperty.ts +++ b/src/transforms/v2-to-v3/modules/removeRequireProperty.ts @@ -10,6 +10,8 @@ export interface RemoveRequireObjectPropertyOptions { sourceValue: string; } +// ToDo: Write generic utility to remove unused modules +// Similar to https://github.com/aws/aws-sdk-js-codemod/pull/781 export const removeRequireProperty = ( j: JSCodeshift, source: Collection, diff --git a/src/transforms/v2-to-v3/modules/requireModule/addNamedModule.ts b/src/transforms/v2-to-v3/modules/requireModule/addNamedModule.ts index e81b67226..d61f4e604 100644 --- a/src/transforms/v2-to-v3/modules/requireModule/addNamedModule.ts +++ b/src/transforms/v2-to-v3/modules/requireModule/addNamedModule.ts @@ -9,9 +9,9 @@ import { } from "jscodeshift"; import { OBJECT_PROPERTY_TYPE_LIST, PACKAGE_NAME, STRING_LITERAL_TYPE_LIST } from "../../config"; -import { getRequireDeclarators } from "../getRequireDeclarators"; import { getRequireProperty } from "../getRequireProperty"; import { objectPatternPropertyCompareFn } from "../objectPatternPropertyCompareFn"; +import { getRequireDeclarators } from "../requireModule"; import { ModulesOptions } from "../types"; export const addNamedModule = ( diff --git a/src/transforms/v2-to-v3/modules/requireModule/getImportSpecifiers.ts b/src/transforms/v2-to-v3/modules/requireModule/getImportSpecifiers.ts new file mode 100644 index 000000000..952cfd53a --- /dev/null +++ b/src/transforms/v2-to-v3/modules/requireModule/getImportSpecifiers.ts @@ -0,0 +1,62 @@ +import { Collection, Identifier, JSCodeshift, ObjectProperty, Property } from "jscodeshift"; +import { OBJECT_PROPERTY_TYPE_LIST } from "../../config"; +import { ImportSpecifierPattern, ImportSpecifierType } from "../types"; +import { getRequireDeclarators } from "./getRequireDeclarators"; + +const getImportSpecifiersFromObjectPattern = (properties: (Property | ObjectProperty)[]) => { + const importSpecifiers = new Set(); + + for (const property of properties) { + if (!OBJECT_PROPERTY_TYPE_LIST.includes(property.type)) { + continue; + } + const objectProperty = property as Property | ObjectProperty; + const key = objectProperty.key; + const value = objectProperty.value; + if (key.type === "Identifier" && value.type === "Identifier") { + importSpecifiers.add({ + importedName: key.name, + localName: value.name, + }); + } + } + + return Array.from(importSpecifiers); +}; + +export const getImportSpecifiers = ( + j: JSCodeshift, + source: Collection, + path?: string +): ImportSpecifierType[] => { + const importSpecifiers = new Set(); + + for (const varDeclarator of getRequireDeclarators(j, source, path).nodes()) { + const declaratorId = varDeclarator.id; + const declaratorInit = varDeclarator.init; + + if (declaratorId.type === "Identifier") { + const declaratorIdName = declaratorId.name; + if (declaratorInit!.type === "MemberExpression") { + importSpecifiers.add({ + importedName: (declaratorInit.property as Identifier).name, + localName: declaratorIdName, + }); + } else { + importSpecifiers.add(declaratorIdName); + } + } + + if (declaratorId.type === "ObjectPattern") { + if (declaratorInit!.type !== "CallExpression") { + continue; + } + const properties = declaratorId.properties as (Property | ObjectProperty)[]; + for (const importSpecifier of getImportSpecifiersFromObjectPattern(properties)) { + importSpecifiers.add(importSpecifier); + } + } + } + + return Array.from(importSpecifiers); +}; diff --git a/src/transforms/v2-to-v3/modules/requireModule/getRequireDeclarators.ts b/src/transforms/v2-to-v3/modules/requireModule/getRequireDeclarators.ts new file mode 100644 index 000000000..435d8301f --- /dev/null +++ b/src/transforms/v2-to-v3/modules/requireModule/getRequireDeclarators.ts @@ -0,0 +1,45 @@ +import { CallExpression, Collection, JSCodeshift, Literal, VariableDeclarator } from "jscodeshift"; +import { PACKAGE_NAME } from "../../config"; + +const isValidRequireCallExpression = (callExpression: CallExpression, path?: string) => { + if (callExpression.arguments.length !== 1) { + return false; + } + + const { value: sourceValue } = callExpression.arguments[0] as Literal; + if (typeof sourceValue !== "string") { + return false; + } + + if (path) { + return sourceValue === path; + } + + return sourceValue.startsWith(PACKAGE_NAME); +}; + +export const getRequireDeclarators = ( + j: JSCodeshift, + source: Collection, + path?: string +): Collection => + source.find(j.VariableDeclarator).filter((varDeclarator) => { + const declaratorInit = varDeclarator.value.init; + if (!declaratorInit) { + return false; + } + + switch (declaratorInit.type) { + case "CallExpression": + return isValidRequireCallExpression(declaratorInit, path); + case "MemberExpression": { + const declaratorInitObject = declaratorInit.object; + if (declaratorInitObject.type !== "CallExpression") { + return false; + } + return isValidRequireCallExpression(declaratorInitObject, path); + } + default: + return false; + } + }); diff --git a/src/transforms/v2-to-v3/modules/requireModule/index.ts b/src/transforms/v2-to-v3/modules/requireModule/index.ts index 0a5092ed4..21d9cc99a 100644 --- a/src/transforms/v2-to-v3/modules/requireModule/index.ts +++ b/src/transforms/v2-to-v3/modules/requireModule/index.ts @@ -1 +1,3 @@ export * from "./addNamedModule"; +export * from "./getImportSpecifiers"; +export * from "./getRequireDeclarators"; diff --git a/src/transforms/v2-to-v3/modules/types.ts b/src/transforms/v2-to-v3/modules/types.ts index 21bdfff4e..720e6e027 100644 --- a/src/transforms/v2-to-v3/modules/types.ts +++ b/src/transforms/v2-to-v3/modules/types.ts @@ -10,12 +10,16 @@ export interface ClientModulesOptions { importType: ImportType; } -export interface ImportSpecifierOptions { +export interface ImportSpecifierPattern { importedName: string; localName?: string; } -export interface ModulesOptions extends ImportSpecifierOptions { +export type ImportSpecifierDefault = string; + +export type ImportSpecifierType = ImportSpecifierPattern | ImportSpecifierDefault; + +export interface ModulesOptions extends ImportSpecifierPattern { packageName: string; }