Skip to content

Commit

Permalink
feat: add is-boolean-object codemod
Browse files Browse the repository at this point in the history
  • Loading branch information
thepassle committed Jul 17, 2024
1 parent 59b061e commit 53bfeeb
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 10 deletions.
3 changes: 3 additions & 0 deletions codemods/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import isWhitespace from './is-whitespace/index.js';

import isArrayBuffer from './is-array-buffer/index.js';

import isBooleanObject from './is-boolean-object/index.js';

export const codemods = {
'is-whitespace': isWhitespace,
'is-array-buffer': isArrayBuffer,
'is-boolean-object': isBooleanObject,
};
19 changes: 11 additions & 8 deletions codemods/is-array-buffer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,16 @@ export default function (options) {
}
}
});

// Remove ESM import statement for 'is-array-buffer'
root.find(j.ImportDeclaration, {
source: { value: 'is-array-buffer' }
}).forEach(path => {
j(path).remove();
dirtyFlag = true;
});

// Remove ESM import statement for 'is-array-buffer'
root
.find(j.ImportDeclaration, {
source: { value: 'is-array-buffer' },
})
.forEach((path) => {
j(path).remove();
dirtyFlag = true;
});

// Replace isArrayBuffer calls with (foo instanceof ArrayBuffer)
root
Expand All @@ -68,6 +70,7 @@ export default function (options) {
const arg = args[0];
const newExpression = j.binaryExpression(
'instanceof',
// @ts-expect-error
arg,
j.identifier('ArrayBuffer'),
);
Expand Down
95 changes: 95 additions & 0 deletions codemods/is-boolean-object/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* @typedef {import('../../types.js').Codemod} Codemod
* @typedef {import('../../types.js').CodemodOptions} CodemodOptions
*/

/**
* @param {CodemodOptions} [options]
* @returns {Codemod}
*/
export default function (options) {
return {
name: 'is-boolean-object',
transform: ({ file, jscodeshift }) => {
const j = jscodeshift;
const root = j(file.source);
let dirtyFlag = false;

// Find all require statements for 'is-boolean-object'
root
.find(j.VariableDeclaration)
.filter((path) =>
path.value.declarations.some(
(decl) =>
j.VariableDeclarator.check(decl) &&
decl.init &&
j.CallExpression.check(decl.init) &&
j.Identifier.check(decl.init.callee) &&
decl.init.callee.name === 'require' &&
decl.init.arguments.length > 0 &&
j.Literal.check(decl.init.arguments[0]) &&
decl.init.arguments[0].value === 'is-boolean-object',
),
)
.forEach((path) => {
// Remove the require statement for 'is-boolean-object'
path.prune();
dirtyFlag = true;
});

// Remove ESM import statement for 'is-boolean-object'
root
.find(j.ImportDeclaration, {
source: { value: 'is-boolean-object' },
})
.forEach((path) => {
j(path).remove();
dirtyFlag = true;
});

// Replace all calls to isBoolean with Object.prototype.toString.call
root
.find(j.CallExpression, {
callee: {
type: 'Identifier',
name: 'isBoolean',
},
})
.replaceWith((path) => {
const arg = path.node.arguments[0];
return j.callExpression(
j.memberExpression(
j.memberExpression(
j.memberExpression(
j.identifier('Object'),
j.identifier('prototype'),
),
j.identifier('toString'),
),
j.identifier('call'),
),
[arg],
);
})
.forEach((path) => {
const parent = path.parent.node;
if (j.BinaryExpression.check(parent)) {
parent.operator = '===';
parent.right = j.literal('[object Boolean]');
} else if (
j.CallExpression.check(parent) &&
parent.arguments.length === 1
) {
const newExpression = j.binaryExpression(
'===',
path.node,
j.literal('[object Boolean]'),
);
parent.arguments[0] = newExpression;
}
});

return dirtyFlag ? root.toSource({ quote: 'single' }) : file.source;
},
};
}
2 changes: 0 additions & 2 deletions scripts/scaffold-codemod.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ const name = process.argv[2];
/** @param {string} s */
const camelize = (s) => s.replace(/-./g, (x) => x[1].toUpperCase());

console.log(fs.readdirSync('./test/fixtures'));

fs.mkdirSync(`./test/fixtures/${name}/case-1`, { recursive: true });
fs.writeFileSync(
`./test/fixtures/${name}/case-1/before.js`,
Expand Down
20 changes: 20 additions & 0 deletions test/fixtures/is-boolean-object/case-1/after.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
var assert = require('assert');

assert.notOk(Object.prototype.toString.call(undefined) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(null) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call('foo') === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(function () {}) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call([]) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call({}) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(/a/g) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(new RegExp('a', 'g')) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(new Date()) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(42) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(NaN) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(Infinity) === '[object Boolean]');

assert.ok(Object.prototype.toString.call(new Boolean(42)) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(false) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(Object(false)) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(true) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(Object(true)) === '[object Boolean]');
21 changes: 21 additions & 0 deletions test/fixtures/is-boolean-object/case-1/before.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
var assert = require('assert');
var isBoolean = require('is-boolean-object');

assert.notOk(isBoolean(undefined));
assert.notOk(isBoolean(null));
assert.notOk(isBoolean('foo'));
assert.notOk(isBoolean(function () {}));
assert.notOk(isBoolean([]));
assert.notOk(isBoolean({}));
assert.notOk(isBoolean(/a/g));
assert.notOk(isBoolean(new RegExp('a', 'g')));
assert.notOk(isBoolean(new Date()));
assert.notOk(isBoolean(42));
assert.notOk(isBoolean(NaN));
assert.notOk(isBoolean(Infinity));

assert.ok(isBoolean(new Boolean(42)));
assert.ok(isBoolean(false));
assert.ok(isBoolean(Object(false)));
assert.ok(isBoolean(true));
assert.ok(isBoolean(Object(true)));
20 changes: 20 additions & 0 deletions test/fixtures/is-boolean-object/case-1/result.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
var assert = require('assert');

assert.notOk(Object.prototype.toString.call(undefined) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(null) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call('foo') === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(function () {}) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call([]) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call({}) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(/a/g) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(new RegExp('a', 'g')) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(new Date()) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(42) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(NaN) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(Infinity) === '[object Boolean]');

assert.ok(Object.prototype.toString.call(new Boolean(42)) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(false) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(Object(false)) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(true) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(Object(true)) === '[object Boolean]');
20 changes: 20 additions & 0 deletions test/fixtures/is-boolean-object/case-2/after.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import assert from 'assert';

assert.notOk(Object.prototype.toString.call(undefined) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(null) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call('foo') === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(function () {}) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call([]) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call({}) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(/a/g) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(new RegExp('a', 'g')) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(new Date()) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(42) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(NaN) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(Infinity) === '[object Boolean]');

assert.ok(Object.prototype.toString.call(new Boolean(42)) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(false) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(Object(false)) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(true) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(Object(true)) === '[object Boolean]');
21 changes: 21 additions & 0 deletions test/fixtures/is-boolean-object/case-2/before.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import assert from 'assert';
import isBoolean from 'is-boolean-object';

assert.notOk(isBoolean(undefined));
assert.notOk(isBoolean(null));
assert.notOk(isBoolean('foo'));
assert.notOk(isBoolean(function () {}));
assert.notOk(isBoolean([]));
assert.notOk(isBoolean({}));
assert.notOk(isBoolean(/a/g));
assert.notOk(isBoolean(new RegExp('a', 'g')));
assert.notOk(isBoolean(new Date()));
assert.notOk(isBoolean(42));
assert.notOk(isBoolean(NaN));
assert.notOk(isBoolean(Infinity));

assert.ok(isBoolean(new Boolean(42)));
assert.ok(isBoolean(false));
assert.ok(isBoolean(Object(false)));
assert.ok(isBoolean(true));
assert.ok(isBoolean(Object(true)));
20 changes: 20 additions & 0 deletions test/fixtures/is-boolean-object/case-2/result.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import assert from 'assert';

assert.notOk(Object.prototype.toString.call(undefined) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(null) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call('foo') === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(function () {}) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call([]) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call({}) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(/a/g) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(new RegExp('a', 'g')) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(new Date()) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(42) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(NaN) === '[object Boolean]');
assert.notOk(Object.prototype.toString.call(Infinity) === '[object Boolean]');

assert.ok(Object.prototype.toString.call(new Boolean(42)) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(false) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(Object(false)) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(true) === '[object Boolean]');
assert.ok(Object.prototype.toString.call(Object(true)) === '[object Boolean]');

0 comments on commit 53bfeeb

Please sign in to comment.