From c1b91df93787a173dee46ccd6e454a72b913fcb2 Mon Sep 17 00:00:00 2001
From: 43081j <43081j@users.noreply.github.com>
Date: Wed, 21 Aug 2024 17:20:21 +0100
Subject: [PATCH] feat: add object.assign codemod

Adds a codemod for `object.assign`.

This also changes behaviour of `removeImport` to consider _all_ require
calls with the target source, rather than only those which are directly
call expressions.
---
 codemods/object.assign/index.js               | 38 +++++++++++++++++++
 codemods/shared.js                            |  8 ++--
 index.js                                      |  2 +
 .../object.assign/get-polyfill/after.js       |  8 ++++
 .../object.assign/get-polyfill/before.js      | 10 +++++
 .../object.assign/get-polyfill/result.js      |  8 ++++
 test/fixtures/object.assign/shim/after.js     |  8 ++++
 test/fixtures/object.assign/shim/before.js    | 10 +++++
 test/fixtures/object.assign/shim/result.js    |  8 ++++
 .../object.assign/simple-cjs/after.js         |  8 ++++
 .../object.assign/simple-cjs/before.js        | 10 +++++
 .../object.assign/simple-cjs/result.js        |  8 ++++
 test/fixtures/object.assign/simple/after.js   |  8 ++++
 test/fixtures/object.assign/simple/before.js  | 10 +++++
 test/fixtures/object.assign/simple/result.js  |  8 ++++
 types/codemods/object.assign/index.d.ts       | 11 ++++++
 16 files changed, 159 insertions(+), 4 deletions(-)
 create mode 100644 codemods/object.assign/index.js
 create mode 100644 test/fixtures/object.assign/get-polyfill/after.js
 create mode 100644 test/fixtures/object.assign/get-polyfill/before.js
 create mode 100644 test/fixtures/object.assign/get-polyfill/result.js
 create mode 100644 test/fixtures/object.assign/shim/after.js
 create mode 100644 test/fixtures/object.assign/shim/before.js
 create mode 100644 test/fixtures/object.assign/shim/result.js
 create mode 100644 test/fixtures/object.assign/simple-cjs/after.js
 create mode 100644 test/fixtures/object.assign/simple-cjs/before.js
 create mode 100644 test/fixtures/object.assign/simple-cjs/result.js
 create mode 100644 test/fixtures/object.assign/simple/after.js
 create mode 100644 test/fixtures/object.assign/simple/before.js
 create mode 100644 test/fixtures/object.assign/simple/result.js
 create mode 100644 types/codemods/object.assign/index.d.ts

diff --git a/codemods/object.assign/index.js b/codemods/object.assign/index.js
new file mode 100644
index 0000000..ea221e5
--- /dev/null
+++ b/codemods/object.assign/index.js
@@ -0,0 +1,38 @@
+import jscodeshift from 'jscodeshift';
+import { removeImport } from '../shared.js';
+
+/**
+ * @typedef {import('../../types.js').Codemod} Codemod
+ * @typedef {import('../../types.js').CodemodOptions} CodemodOptions
+ */
+
+/**
+ * @param {CodemodOptions} [options]
+ * @returns {Codemod}
+ */
+export default function (options) {
+	return {
+		name: 'object.assign',
+		transform: ({ file }) => {
+			const j = jscodeshift;
+			const root = j(file.source);
+
+			const { identifier } = removeImport('object.assign', root, j);
+
+			root
+				.find(j.CallExpression, {
+					callee: {
+						name: identifier,
+					},
+				})
+				.replaceWith(({ node }) => {
+					return j.callExpression(
+						j.memberExpression(j.identifier('Object'), j.identifier('assign')),
+						node.arguments,
+					);
+				});
+
+			return root.toSource(options);
+		},
+	};
+}
diff --git a/codemods/shared.js b/codemods/shared.js
index ec865f1..55c0290 100644
--- a/codemods/shared.js
+++ b/codemods/shared.js
@@ -21,8 +21,8 @@ export function removeImport(name, root, j) {
 		},
 	});
 
-	const requireDeclaration = root.find(j.VariableDeclarator, {
-		init: {
+	const requireDeclaration = root
+		.find(j.CallExpression, {
 			callee: {
 				name: 'require',
 			},
@@ -31,8 +31,8 @@ export function removeImport(name, root, j) {
 					value: name,
 				},
 			],
-		},
-	});
+		})
+		.closest(j.VariableDeclarator);
 
 	// Require statements without declarations like `Object.is = require("object-is");`
 	const requireAssignment = root.find(j.AssignmentExpression, {
diff --git a/index.js b/index.js
index d2ad16d..5adf40c 100644
--- a/index.js
+++ b/index.js
@@ -109,6 +109,7 @@ import numberPrototypeToexponential from './codemods/number.prototype.toexponent
 import objectAssign from './codemods/object-assign/index.js';
 import objectIs from './codemods/object-is/index.js';
 import objectKeys from './codemods/object-keys/index.js';
+import objectAssign2 from './codemods/object.assign/index.js';
 import objectDefineproperties from './codemods/object.defineproperties/index.js';
 import objectEntries from './codemods/object.entries/index.js';
 import objectFromentries from './codemods/object.fromentries/index.js';
@@ -273,6 +274,7 @@ export const codemods = {
   "object-assign": objectAssign,
   "object-is": objectIs,
   "object-keys": objectKeys,
+  "object.assign": objectAssign2,
   "object.defineproperties": objectDefineproperties,
   "object.entries": objectEntries,
   "object.fromentries": objectFromentries,
diff --git a/test/fixtures/object.assign/get-polyfill/after.js b/test/fixtures/object.assign/get-polyfill/after.js
new file mode 100644
index 0000000..80b5244
--- /dev/null
+++ b/test/fixtures/object.assign/get-polyfill/after.js
@@ -0,0 +1,8 @@
+Object.assign({}, {foo: 303}, {bar: 808});
+
+Object.assign({}, {});
+
+const foo = {};
+const bar = {};
+
+Object.assign(foo, bar);
diff --git a/test/fixtures/object.assign/get-polyfill/before.js b/test/fixtures/object.assign/get-polyfill/before.js
new file mode 100644
index 0000000..403196c
--- /dev/null
+++ b/test/fixtures/object.assign/get-polyfill/before.js
@@ -0,0 +1,10 @@
+const assign = require('object.assign').getPolyfill();
+
+assign({}, {foo: 303}, {bar: 808});
+
+assign({}, {});
+
+const foo = {};
+const bar = {};
+
+assign(foo, bar);
diff --git a/test/fixtures/object.assign/get-polyfill/result.js b/test/fixtures/object.assign/get-polyfill/result.js
new file mode 100644
index 0000000..80b5244
--- /dev/null
+++ b/test/fixtures/object.assign/get-polyfill/result.js
@@ -0,0 +1,8 @@
+Object.assign({}, {foo: 303}, {bar: 808});
+
+Object.assign({}, {});
+
+const foo = {};
+const bar = {};
+
+Object.assign(foo, bar);
diff --git a/test/fixtures/object.assign/shim/after.js b/test/fixtures/object.assign/shim/after.js
new file mode 100644
index 0000000..80b5244
--- /dev/null
+++ b/test/fixtures/object.assign/shim/after.js
@@ -0,0 +1,8 @@
+Object.assign({}, {foo: 303}, {bar: 808});
+
+Object.assign({}, {});
+
+const foo = {};
+const bar = {};
+
+Object.assign(foo, bar);
diff --git a/test/fixtures/object.assign/shim/before.js b/test/fixtures/object.assign/shim/before.js
new file mode 100644
index 0000000..0f38393
--- /dev/null
+++ b/test/fixtures/object.assign/shim/before.js
@@ -0,0 +1,10 @@
+const assign = require('object.assign').shim();
+
+assign({}, {foo: 303}, {bar: 808});
+
+assign({}, {});
+
+const foo = {};
+const bar = {};
+
+assign(foo, bar);
diff --git a/test/fixtures/object.assign/shim/result.js b/test/fixtures/object.assign/shim/result.js
new file mode 100644
index 0000000..80b5244
--- /dev/null
+++ b/test/fixtures/object.assign/shim/result.js
@@ -0,0 +1,8 @@
+Object.assign({}, {foo: 303}, {bar: 808});
+
+Object.assign({}, {});
+
+const foo = {};
+const bar = {};
+
+Object.assign(foo, bar);
diff --git a/test/fixtures/object.assign/simple-cjs/after.js b/test/fixtures/object.assign/simple-cjs/after.js
new file mode 100644
index 0000000..80b5244
--- /dev/null
+++ b/test/fixtures/object.assign/simple-cjs/after.js
@@ -0,0 +1,8 @@
+Object.assign({}, {foo: 303}, {bar: 808});
+
+Object.assign({}, {});
+
+const foo = {};
+const bar = {};
+
+Object.assign(foo, bar);
diff --git a/test/fixtures/object.assign/simple-cjs/before.js b/test/fixtures/object.assign/simple-cjs/before.js
new file mode 100644
index 0000000..ba9f1b9
--- /dev/null
+++ b/test/fixtures/object.assign/simple-cjs/before.js
@@ -0,0 +1,10 @@
+const assign = require('object.assign');
+
+assign({}, {foo: 303}, {bar: 808});
+
+assign({}, {});
+
+const foo = {};
+const bar = {};
+
+assign(foo, bar);
diff --git a/test/fixtures/object.assign/simple-cjs/result.js b/test/fixtures/object.assign/simple-cjs/result.js
new file mode 100644
index 0000000..80b5244
--- /dev/null
+++ b/test/fixtures/object.assign/simple-cjs/result.js
@@ -0,0 +1,8 @@
+Object.assign({}, {foo: 303}, {bar: 808});
+
+Object.assign({}, {});
+
+const foo = {};
+const bar = {};
+
+Object.assign(foo, bar);
diff --git a/test/fixtures/object.assign/simple/after.js b/test/fixtures/object.assign/simple/after.js
new file mode 100644
index 0000000..80b5244
--- /dev/null
+++ b/test/fixtures/object.assign/simple/after.js
@@ -0,0 +1,8 @@
+Object.assign({}, {foo: 303}, {bar: 808});
+
+Object.assign({}, {});
+
+const foo = {};
+const bar = {};
+
+Object.assign(foo, bar);
diff --git a/test/fixtures/object.assign/simple/before.js b/test/fixtures/object.assign/simple/before.js
new file mode 100644
index 0000000..2245e0e
--- /dev/null
+++ b/test/fixtures/object.assign/simple/before.js
@@ -0,0 +1,10 @@
+import assign from 'object.assign';
+
+assign({}, {foo: 303}, {bar: 808});
+
+assign({}, {});
+
+const foo = {};
+const bar = {};
+
+assign(foo, bar);
diff --git a/test/fixtures/object.assign/simple/result.js b/test/fixtures/object.assign/simple/result.js
new file mode 100644
index 0000000..80b5244
--- /dev/null
+++ b/test/fixtures/object.assign/simple/result.js
@@ -0,0 +1,8 @@
+Object.assign({}, {foo: 303}, {bar: 808});
+
+Object.assign({}, {});
+
+const foo = {};
+const bar = {};
+
+Object.assign(foo, bar);
diff --git a/types/codemods/object.assign/index.d.ts b/types/codemods/object.assign/index.d.ts
new file mode 100644
index 0000000..9dcfef7
--- /dev/null
+++ b/types/codemods/object.assign/index.d.ts
@@ -0,0 +1,11 @@
+/**
+ * @typedef {import('../../types.js').Codemod} Codemod
+ * @typedef {import('../../types.js').CodemodOptions} CodemodOptions
+ */
+/**
+ * @param {CodemodOptions} [options]
+ * @returns {Codemod}
+ */
+export default function _default(options?: import("../../types.js").CodemodOptions | undefined): Codemod;
+export type Codemod = import("../../types.js").Codemod;
+export type CodemodOptions = import("../../types.js").CodemodOptions;