Skip to content

Commit

Permalink
feat: add codemod for is-plain-object
Browse files Browse the repository at this point in the history
  • Loading branch information
Brady Blair committed Jul 23, 2024
1 parent 8537550 commit 27692b9
Show file tree
Hide file tree
Showing 7 changed files with 393 additions and 0 deletions.
105 changes: 105 additions & 0 deletions codemods/is-plain-object/index.js
Original file line number Diff line number Diff line change
@@ -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;
},
};
}
57 changes: 57 additions & 0 deletions test/fixtures/is-plain-object/case-1/after.js
Original file line number Diff line number Diff line change
@@ -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);
30 changes: 30 additions & 0 deletions test/fixtures/is-plain-object/case-1/before.js
Original file line number Diff line number Diff line change
@@ -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);
57 changes: 57 additions & 0 deletions test/fixtures/is-plain-object/case-1/result.js
Original file line number Diff line number Diff line change
@@ -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);
57 changes: 57 additions & 0 deletions test/fixtures/is-plain-object/case-2/after.js
Original file line number Diff line number Diff line change
@@ -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);
30 changes: 30 additions & 0 deletions test/fixtures/is-plain-object/case-2/before.js
Original file line number Diff line number Diff line change
@@ -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);
Loading

0 comments on commit 27692b9

Please sign in to comment.