From b1300ada0298e7365513fcb30efc33fb0d0a14ea Mon Sep 17 00:00:00 2001 From: thepassle Date: Wed, 17 Jul 2024 19:02:09 +0200 Subject: [PATCH] feat: add is-string --- codemods/index.js | 5 +- codemods/is-string/index.js | 67 ++++++++++++++++++++++++ test/fixtures/is-string/case-1/after.js | 19 +++++++ test/fixtures/is-string/case-1/before.js | 20 +++++++ test/fixtures/is-string/case-1/result.js | 19 +++++++ test/fixtures/is-string/case-2/after.js | 19 +++++++ test/fixtures/is-string/case-2/before.js | 20 +++++++ test/fixtures/is-string/case-2/result.js | 19 +++++++ 8 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 codemods/is-string/index.js create mode 100644 test/fixtures/is-string/case-1/after.js create mode 100644 test/fixtures/is-string/case-1/before.js create mode 100644 test/fixtures/is-string/case-1/result.js create mode 100644 test/fixtures/is-string/case-2/after.js create mode 100644 test/fixtures/is-string/case-2/before.js create mode 100644 test/fixtures/is-string/case-2/result.js diff --git a/codemods/index.js b/codemods/index.js index f4f8318..1b593c7 100644 --- a/codemods/index.js +++ b/codemods/index.js @@ -8,10 +8,13 @@ import isDateObject from './is-date-object/index.js'; import isNumberObject from './is-number-object/index.js'; +import isString from './is-string/index.js'; + export const codemods = { 'is-whitespace': isWhitespace, 'is-array-buffer': isArrayBuffer, 'is-boolean-object': isBooleanObject, 'is-date-object': isDateObject, - 'is-number-object': isNumberObject + 'is-number-object': isNumberObject, + 'is-string': isString }; diff --git a/codemods/is-string/index.js b/codemods/is-string/index.js new file mode 100644 index 0000000..4223a38 --- /dev/null +++ b/codemods/is-string/index.js @@ -0,0 +1,67 @@ +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-string', + transform: ({ file }) => { + const j = jscodeshift; + const root = j(file.source); + + removeImport('is-string', root, j); + + // Replace all calls to isString with Object.prototype.toString.call + root + .find(j.CallExpression, { + callee: { + type: 'Identifier', + name: 'isString', + }, + }) + .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 String]'); + } else if ( + j.CallExpression.check(parent) && + parent.arguments.length === 1 + ) { + const newExpression = j.binaryExpression( + '===', + path.node, + j.literal('[object String]'), + ); + parent.arguments[0] = newExpression; + } + }); + + return root.toSource({ quote: 'single' }); + }, + }; +} diff --git a/test/fixtures/is-string/case-1/after.js b/test/fixtures/is-string/case-1/after.js new file mode 100644 index 0000000..6db389e --- /dev/null +++ b/test/fixtures/is-string/case-1/after.js @@ -0,0 +1,19 @@ +var assert = require('assert'); + +assert.notOk(Object.prototype.toString.call(undefined) === '[object String]'); +assert.notOk(Object.prototype.toString.call(null) === '[object String]'); +assert.notOk(Object.prototype.toString.call(false) === '[object String]'); +assert.notOk(Object.prototype.toString.call(true) === '[object String]'); +assert.notOk(Object.prototype.toString.call(function () {}) === '[object String]'); +assert.notOk(Object.prototype.toString.call([]) === '[object String]'); +assert.notOk(Object.prototype.toString.call({}) === '[object String]'); +assert.notOk(Object.prototype.toString.call(/a/g) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new RegExp('a', 'g')) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new Date()) === '[object String]'); +assert.notOk(Object.prototype.toString.call(42) === '[object String]'); +assert.notOk(Object.prototype.toString.call(NaN) === '[object String]'); +assert.notOk(Object.prototype.toString.call(Infinity) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new Number(42)) === '[object String]'); + +assert.ok(Object.prototype.toString.call('foo') === '[object String]'); +assert.ok(Object.prototype.toString.call(Object('foo')) === '[object String]'); diff --git a/test/fixtures/is-string/case-1/before.js b/test/fixtures/is-string/case-1/before.js new file mode 100644 index 0000000..829e89a --- /dev/null +++ b/test/fixtures/is-string/case-1/before.js @@ -0,0 +1,20 @@ +var isString = require('is-string'); +var assert = require('assert'); + +assert.notOk(isString(undefined)); +assert.notOk(isString(null)); +assert.notOk(isString(false)); +assert.notOk(isString(true)); +assert.notOk(isString(function () {})); +assert.notOk(isString([])); +assert.notOk(isString({})); +assert.notOk(isString(/a/g)); +assert.notOk(isString(new RegExp('a', 'g'))); +assert.notOk(isString(new Date())); +assert.notOk(isString(42)); +assert.notOk(isString(NaN)); +assert.notOk(isString(Infinity)); +assert.notOk(isString(new Number(42))); + +assert.ok(isString('foo')); +assert.ok(isString(Object('foo'))); diff --git a/test/fixtures/is-string/case-1/result.js b/test/fixtures/is-string/case-1/result.js new file mode 100644 index 0000000..6db389e --- /dev/null +++ b/test/fixtures/is-string/case-1/result.js @@ -0,0 +1,19 @@ +var assert = require('assert'); + +assert.notOk(Object.prototype.toString.call(undefined) === '[object String]'); +assert.notOk(Object.prototype.toString.call(null) === '[object String]'); +assert.notOk(Object.prototype.toString.call(false) === '[object String]'); +assert.notOk(Object.prototype.toString.call(true) === '[object String]'); +assert.notOk(Object.prototype.toString.call(function () {}) === '[object String]'); +assert.notOk(Object.prototype.toString.call([]) === '[object String]'); +assert.notOk(Object.prototype.toString.call({}) === '[object String]'); +assert.notOk(Object.prototype.toString.call(/a/g) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new RegExp('a', 'g')) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new Date()) === '[object String]'); +assert.notOk(Object.prototype.toString.call(42) === '[object String]'); +assert.notOk(Object.prototype.toString.call(NaN) === '[object String]'); +assert.notOk(Object.prototype.toString.call(Infinity) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new Number(42)) === '[object String]'); + +assert.ok(Object.prototype.toString.call('foo') === '[object String]'); +assert.ok(Object.prototype.toString.call(Object('foo')) === '[object String]'); diff --git a/test/fixtures/is-string/case-2/after.js b/test/fixtures/is-string/case-2/after.js new file mode 100644 index 0000000..321b252 --- /dev/null +++ b/test/fixtures/is-string/case-2/after.js @@ -0,0 +1,19 @@ +import assert from 'assert'; + +assert.notOk(Object.prototype.toString.call(undefined) === '[object String]'); +assert.notOk(Object.prototype.toString.call(null) === '[object String]'); +assert.notOk(Object.prototype.toString.call(false) === '[object String]'); +assert.notOk(Object.prototype.toString.call(true) === '[object String]'); +assert.notOk(Object.prototype.toString.call(function () {}) === '[object String]'); +assert.notOk(Object.prototype.toString.call([]) === '[object String]'); +assert.notOk(Object.prototype.toString.call({}) === '[object String]'); +assert.notOk(Object.prototype.toString.call(/a/g) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new RegExp('a', 'g')) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new Date()) === '[object String]'); +assert.notOk(Object.prototype.toString.call(42) === '[object String]'); +assert.notOk(Object.prototype.toString.call(NaN) === '[object String]'); +assert.notOk(Object.prototype.toString.call(Infinity) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new Number(42)) === '[object String]'); + +assert.ok(Object.prototype.toString.call('foo') === '[object String]'); +assert.ok(Object.prototype.toString.call(Object('foo')) === '[object String]'); diff --git a/test/fixtures/is-string/case-2/before.js b/test/fixtures/is-string/case-2/before.js new file mode 100644 index 0000000..ebbf467 --- /dev/null +++ b/test/fixtures/is-string/case-2/before.js @@ -0,0 +1,20 @@ +import assert from 'assert'; +import isString from 'is-string'; + +assert.notOk(isString(undefined)); +assert.notOk(isString(null)); +assert.notOk(isString(false)); +assert.notOk(isString(true)); +assert.notOk(isString(function () {})); +assert.notOk(isString([])); +assert.notOk(isString({})); +assert.notOk(isString(/a/g)); +assert.notOk(isString(new RegExp('a', 'g'))); +assert.notOk(isString(new Date())); +assert.notOk(isString(42)); +assert.notOk(isString(NaN)); +assert.notOk(isString(Infinity)); +assert.notOk(isString(new Number(42))); + +assert.ok(isString('foo')); +assert.ok(isString(Object('foo'))); diff --git a/test/fixtures/is-string/case-2/result.js b/test/fixtures/is-string/case-2/result.js new file mode 100644 index 0000000..321b252 --- /dev/null +++ b/test/fixtures/is-string/case-2/result.js @@ -0,0 +1,19 @@ +import assert from 'assert'; + +assert.notOk(Object.prototype.toString.call(undefined) === '[object String]'); +assert.notOk(Object.prototype.toString.call(null) === '[object String]'); +assert.notOk(Object.prototype.toString.call(false) === '[object String]'); +assert.notOk(Object.prototype.toString.call(true) === '[object String]'); +assert.notOk(Object.prototype.toString.call(function () {}) === '[object String]'); +assert.notOk(Object.prototype.toString.call([]) === '[object String]'); +assert.notOk(Object.prototype.toString.call({}) === '[object String]'); +assert.notOk(Object.prototype.toString.call(/a/g) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new RegExp('a', 'g')) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new Date()) === '[object String]'); +assert.notOk(Object.prototype.toString.call(42) === '[object String]'); +assert.notOk(Object.prototype.toString.call(NaN) === '[object String]'); +assert.notOk(Object.prototype.toString.call(Infinity) === '[object String]'); +assert.notOk(Object.prototype.toString.call(new Number(42)) === '[object String]'); + +assert.ok(Object.prototype.toString.call('foo') === '[object String]'); +assert.ok(Object.prototype.toString.call(Object('foo')) === '[object String]');