diff --git a/codemods/is-plain-object/index.js b/codemods/is-plain-object/index.js new file mode 100644 index 0000000..cb8096f --- /dev/null +++ b/codemods/is-plain-object/index.js @@ -0,0 +1,105 @@ +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: 'is-plain-object', + transform: ({ file }) => { + const j = jscodeshift; + const root = j(file.source); + let isDirty = false; + + const { identifier } = removeImport('is-plain-object', root, j); + + if (identifier) { + const callExpressions = root.find(j.CallExpression, { + callee: { + name: identifier, + }, + }); + + for (const path of callExpressions.paths()) { + const args = path.value.arguments; + if (args.length > 0) { + isDirty = true; + + const arg = + args[0].type === 'SpreadElement' ? args[0].argument : args[0]; + + const newExpression = j.callExpression( + j.arrowFunctionExpression( + [j.identifier('v')], + j.blockStatement([ + j.returnStatement( + j.unaryExpression( + '!', + j.unaryExpression( + '!', + j.logicalExpression( + '&&', + j.identifier('v'), + j.logicalExpression( + '&&', + j.binaryExpression( + '===', + j.unaryExpression('typeof', j.identifier('v')), + j.literal('object'), + ), + j.parenthesizedExpression( + j.logicalExpression( + '||', + j.binaryExpression( + '===', + j.callExpression( + j.memberExpression( + j.identifier('Object'), + j.identifier('getPrototypeOf'), + ), + [j.identifier('v')], + ), + j.literal(null), + ), + j.binaryExpression( + '===', + j.callExpression( + j.memberExpression( + j.identifier('Object'), + j.identifier('getPrototypeOf'), + ), + [j.identifier('v')], + ), + j.memberExpression( + j.identifier('Object'), + j.identifier('prototype'), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ]), + ), + [arg], + ); + + j(path).replaceWith(newExpression); + } + } + } + + return isDirty ? root.toSource(options) : file.source; + }, + }; +} diff --git a/test/fixtures/is-plain-object/case-1/after.js b/test/fixtures/is-plain-object/case-1/after.js new file mode 100644 index 0000000..867d0bd --- /dev/null +++ b/test/fixtures/is-plain-object/case-1/after.js @@ -0,0 +1,57 @@ +import assert from 'assert'; + +// Test cases +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create({})), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create(Object.prototype)), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({ foo: 'bar' }), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({}), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(null), false); + +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(1), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(['foo', 'bar']), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})([]), false); + +const Foo = function () { + return { foo: Math.random() > 0.5 ? 'bar' : 'baz' }; +}; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Foo), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Foo()), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create(null)), true); + +const bar = { ...{ foo: 'bar' } }; +const baz = { ...bar }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({ ...baz }), true); + +const apple = Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(apple), true); + +const orange = () => Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(orange), false); \ No newline at end of file diff --git a/test/fixtures/is-plain-object/case-1/before.js b/test/fixtures/is-plain-object/case-1/before.js new file mode 100644 index 0000000..2aecf59 --- /dev/null +++ b/test/fixtures/is-plain-object/case-1/before.js @@ -0,0 +1,30 @@ +import assert from 'assert'; +import { isPlainObject as isPlainObj } from 'is-plain-object'; + +// Test cases +assert.strictEqual(isPlainObj(Object.create({})), false); +assert.strictEqual(isPlainObj(Object.create(Object.prototype)), true); +assert.strictEqual(isPlainObj({ foo: 'bar' }), true); +assert.strictEqual(isPlainObj({}), true); +assert.strictEqual(isPlainObj(null), false); + +assert.strictEqual(isPlainObj(1), false); +assert.strictEqual(isPlainObj(['foo', 'bar']), false); +assert.strictEqual(isPlainObj([]), false); + +const Foo = function () { + return { foo: Math.random() > 0.5 ? 'bar' : 'baz' }; +}; +assert.strictEqual(isPlainObj(Foo), false); +assert.strictEqual(isPlainObj(Foo()), true); +assert.strictEqual(isPlainObj(Object.create(null)), true); + +const bar = { ...{ foo: 'bar' } }; +const baz = { ...bar }; +assert.strictEqual(isPlainObj({ ...baz }), true); + +const apple = Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual(isPlainObj(apple), true); + +const orange = () => Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual(isPlainObj(orange), false); \ No newline at end of file diff --git a/test/fixtures/is-plain-object/case-1/result.js b/test/fixtures/is-plain-object/case-1/result.js new file mode 100644 index 0000000..867d0bd --- /dev/null +++ b/test/fixtures/is-plain-object/case-1/result.js @@ -0,0 +1,57 @@ +import assert from 'assert'; + +// Test cases +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create({})), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create(Object.prototype)), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({ foo: 'bar' }), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({}), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(null), false); + +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(1), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(['foo', 'bar']), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})([]), false); + +const Foo = function () { + return { foo: Math.random() > 0.5 ? 'bar' : 'baz' }; +}; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Foo), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Foo()), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create(null)), true); + +const bar = { ...{ foo: 'bar' } }; +const baz = { ...bar }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({ ...baz }), true); + +const apple = Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(apple), true); + +const orange = () => Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(orange), false); \ No newline at end of file diff --git a/test/fixtures/is-plain-object/case-2/after.js b/test/fixtures/is-plain-object/case-2/after.js new file mode 100644 index 0000000..869298a --- /dev/null +++ b/test/fixtures/is-plain-object/case-2/after.js @@ -0,0 +1,57 @@ +const assert = require('assert'); + +// Test cases +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create({})), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create(Object.prototype)), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({ foo: 'bar' }), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({}), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(null), false); + +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(1), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(['foo', 'bar']), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})([]), false); + +const Foo = function () { + return { foo: Math.random() > 0.5 ? 'bar' : 'baz' }; +}; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Foo), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Foo()), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create(null)), true); + +const bar = { ...{ foo: 'bar' } }; +const baz = { ...bar }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({ ...baz }), true); + +const apple = Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(apple), true); + +const orange = () => Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(orange), false); diff --git a/test/fixtures/is-plain-object/case-2/before.js b/test/fixtures/is-plain-object/case-2/before.js new file mode 100644 index 0000000..abf4845 --- /dev/null +++ b/test/fixtures/is-plain-object/case-2/before.js @@ -0,0 +1,30 @@ +const assert = require('assert'); +const { differentNameForModule } = require('is-plain-object'); + +// Test cases +assert.strictEqual(differentNameForModule(Object.create({})), false); +assert.strictEqual(differentNameForModule(Object.create(Object.prototype)), true); +assert.strictEqual(differentNameForModule({ foo: 'bar' }), true); +assert.strictEqual(differentNameForModule({}), true); +assert.strictEqual(differentNameForModule(null), false); + +assert.strictEqual(differentNameForModule(1), false); +assert.strictEqual(differentNameForModule(['foo', 'bar']), false); +assert.strictEqual(differentNameForModule([]), false); + +const Foo = function () { + return { foo: Math.random() > 0.5 ? 'bar' : 'baz' }; +}; +assert.strictEqual(differentNameForModule(Foo), false); +assert.strictEqual(differentNameForModule(Foo()), true); +assert.strictEqual(differentNameForModule(Object.create(null)), true); + +const bar = { ...{ foo: 'bar' } }; +const baz = { ...bar }; +assert.strictEqual(differentNameForModule({ ...baz }), true); + +const apple = Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual(differentNameForModule(apple), true); + +const orange = () => Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual(differentNameForModule(orange), false); diff --git a/test/fixtures/is-plain-object/case-2/result.js b/test/fixtures/is-plain-object/case-2/result.js new file mode 100644 index 0000000..869298a --- /dev/null +++ b/test/fixtures/is-plain-object/case-2/result.js @@ -0,0 +1,57 @@ +const assert = require('assert'); + +// Test cases +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create({})), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create(Object.prototype)), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({ foo: 'bar' }), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({}), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(null), false); + +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(1), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(['foo', 'bar']), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})([]), false); + +const Foo = function () { + return { foo: Math.random() > 0.5 ? 'bar' : 'baz' }; +}; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Foo), false); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Foo()), true); +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(Object.create(null)), true); + +const bar = { ...{ foo: 'bar' } }; +const baz = { ...bar }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})({ ...baz }), true); + +const apple = Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(apple), true); + +const orange = () => Math.random() > 0.5 ? { foo: 'bar' } : { bar: 'baz' }; +assert.strictEqual((v => { + return !!(v && (typeof v === "object" && (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype))); +})(orange), false);