From 4d23152166ca777083c3bf904caf086321973570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Filip?= Date: Sun, 28 Jul 2024 18:05:03 +0200 Subject: [PATCH 1/3] Implement codemod for clone-regexp --- codemods/clone-regexp/index.js | 48 +++++++++++++++++++++ test/fixtures/clone-regexp/case-1/after.js | 13 ++++++ test/fixtures/clone-regexp/case-1/before.js | 14 ++++++ test/fixtures/clone-regexp/case-1/result.js | 13 ++++++ test/fixtures/clone-regexp/case-2/after.js | 17 ++++++++ test/fixtures/clone-regexp/case-2/before.js | 19 ++++++++ test/fixtures/clone-regexp/case-2/result.js | 17 ++++++++ test/fixtures/clone-regexp/case-3/after.js | 1 + test/fixtures/clone-regexp/case-3/before.js | 3 ++ test/fixtures/clone-regexp/case-3/result.js | 1 + test/fixtures/clone-regexp/case-4/after.js | 1 + test/fixtures/clone-regexp/case-4/before.js | 3 ++ test/fixtures/clone-regexp/case-4/result.js | 1 + 13 files changed, 151 insertions(+) create mode 100644 codemods/clone-regexp/index.js create mode 100644 test/fixtures/clone-regexp/case-1/after.js create mode 100644 test/fixtures/clone-regexp/case-1/before.js create mode 100644 test/fixtures/clone-regexp/case-1/result.js create mode 100644 test/fixtures/clone-regexp/case-2/after.js create mode 100644 test/fixtures/clone-regexp/case-2/before.js create mode 100644 test/fixtures/clone-regexp/case-2/result.js create mode 100644 test/fixtures/clone-regexp/case-3/after.js create mode 100644 test/fixtures/clone-regexp/case-3/before.js create mode 100644 test/fixtures/clone-regexp/case-3/result.js create mode 100644 test/fixtures/clone-regexp/case-4/after.js create mode 100644 test/fixtures/clone-regexp/case-4/before.js create mode 100644 test/fixtures/clone-regexp/case-4/result.js diff --git a/codemods/clone-regexp/index.js b/codemods/clone-regexp/index.js new file mode 100644 index 0000000..7d118b7 --- /dev/null +++ b/codemods/clone-regexp/index.js @@ -0,0 +1,48 @@ +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: 'clone-regexp', + transform: ({ file }) => { + const j = jscodeshift; + const root = j(file.source); + + const { identifier } = removeImport('clone-regexp', root, j); + + return root + .find(j.CallExpression, { + callee: { + type: 'Identifier', + name: identifier, + }, + }) + .replaceWith((path) => { + const args = path.node.arguments; + if (args.length === 0) { + return j.newExpression(j.identifier('RegExp'), []); + } + if (args.length === 1) { + return j.newExpression(j.identifier('RegExp'), [args[0]]); + } + if (args.length === 2) { + const newExpression = j.newExpression(j.identifier('RegExp'), [ + args[0], + ]); + newExpression.comments = [j.commentBlock(' Todo ', false, true)]; + return newExpression; + } + }) + .toSource({ quote: 'single' }); + }, + }; +} diff --git a/test/fixtures/clone-regexp/case-1/after.js b/test/fixtures/clone-regexp/case-1/after.js new file mode 100644 index 0000000..4e05d98 --- /dev/null +++ b/test/fixtures/clone-regexp/case-1/after.js @@ -0,0 +1,13 @@ +import assert from 'assert'; + +const regex = /[a-z]/gi; + +// Argument is a regex +const clone1 = new RegExp(/[a-z]/gi); +assert.deepStrictEqual(regex, clone1); +assert.ok(regex !== clone1) + +// Argument is an identifier +const clone2 = new RegExp(regex); +assert.deepStrictEqual(regex, clone2); +assert.ok(regex !== clone2); \ No newline at end of file diff --git a/test/fixtures/clone-regexp/case-1/before.js b/test/fixtures/clone-regexp/case-1/before.js new file mode 100644 index 0000000..031e95a --- /dev/null +++ b/test/fixtures/clone-regexp/case-1/before.js @@ -0,0 +1,14 @@ +import cloneRegexp from 'clone-regexp'; +import assert from 'assert'; + +const regex = /[a-z]/gi; + +// Argument is a regex +const clone1 = cloneRegexp(/[a-z]/gi); +assert.deepStrictEqual(regex, clone1); +assert.ok(regex !== clone1) + +// Argument is an identifier +const clone2 = cloneRegexp(regex); +assert.deepStrictEqual(regex, clone2); +assert.ok(regex !== clone2); \ No newline at end of file diff --git a/test/fixtures/clone-regexp/case-1/result.js b/test/fixtures/clone-regexp/case-1/result.js new file mode 100644 index 0000000..4e05d98 --- /dev/null +++ b/test/fixtures/clone-regexp/case-1/result.js @@ -0,0 +1,13 @@ +import assert from 'assert'; + +const regex = /[a-z]/gi; + +// Argument is a regex +const clone1 = new RegExp(/[a-z]/gi); +assert.deepStrictEqual(regex, clone1); +assert.ok(regex !== clone1) + +// Argument is an identifier +const clone2 = new RegExp(regex); +assert.deepStrictEqual(regex, clone2); +assert.ok(regex !== clone2); \ No newline at end of file diff --git a/test/fixtures/clone-regexp/case-2/after.js b/test/fixtures/clone-regexp/case-2/after.js new file mode 100644 index 0000000..ea05ee5 --- /dev/null +++ b/test/fixtures/clone-regexp/case-2/after.js @@ -0,0 +1,17 @@ +const regex = /[a-z]/gi; + +// Argument is a regex +const clone1 = new RegExp(/[a-z]/gi)/* Todo */; + +// Argument is an identifier +const clone2 = new RegExp(regex)/* Todo */; + +// Both arguments are identifier +const opt = { ignoreCase: 1 === 1 }; +const clone3 = new RegExp(regex)/* Todo */; + +// Multiple cloneRegexp +const clone4 = f(new RegExp(regex)/* Todo */, new RegExp(regex)/* Todo */); + +// Regex comes from a function +const clone5 = new RegExp(f(regex))/* Todo */; \ No newline at end of file diff --git a/test/fixtures/clone-regexp/case-2/before.js b/test/fixtures/clone-regexp/case-2/before.js new file mode 100644 index 0000000..d98af4c --- /dev/null +++ b/test/fixtures/clone-regexp/case-2/before.js @@ -0,0 +1,19 @@ +import cloneRegexp from 'clone-regexp'; + +const regex = /[a-z]/gi; + +// Argument is a regex +const clone1 = cloneRegexp(/[a-z]/gi, { global: false }); + +// Argument is an identifier +const clone2 = cloneRegexp(regex, { unicode: true }); + +// Both arguments are identifier +const opt = { ignoreCase: 1 === 1 }; +const clone3 = cloneRegexp(regex, opt); + +// Multiple cloneRegexp +const clone4 = f(cloneRegexp(regex, { sticky: true }), cloneRegexp(regex, { dotAll: true })); + +// Regex comes from a function +const clone5 = cloneRegexp(f(regex), { multiline: true }); \ No newline at end of file diff --git a/test/fixtures/clone-regexp/case-2/result.js b/test/fixtures/clone-regexp/case-2/result.js new file mode 100644 index 0000000..ea05ee5 --- /dev/null +++ b/test/fixtures/clone-regexp/case-2/result.js @@ -0,0 +1,17 @@ +const regex = /[a-z]/gi; + +// Argument is a regex +const clone1 = new RegExp(/[a-z]/gi)/* Todo */; + +// Argument is an identifier +const clone2 = new RegExp(regex)/* Todo */; + +// Both arguments are identifier +const opt = { ignoreCase: 1 === 1 }; +const clone3 = new RegExp(regex)/* Todo */; + +// Multiple cloneRegexp +const clone4 = f(new RegExp(regex)/* Todo */, new RegExp(regex)/* Todo */); + +// Regex comes from a function +const clone5 = new RegExp(f(regex))/* Todo */; \ No newline at end of file diff --git a/test/fixtures/clone-regexp/case-3/after.js b/test/fixtures/clone-regexp/case-3/after.js new file mode 100644 index 0000000..3281073 --- /dev/null +++ b/test/fixtures/clone-regexp/case-3/after.js @@ -0,0 +1 @@ +const clone1 = new RegExp(); diff --git a/test/fixtures/clone-regexp/case-3/before.js b/test/fixtures/clone-regexp/case-3/before.js new file mode 100644 index 0000000..90121cc --- /dev/null +++ b/test/fixtures/clone-regexp/case-3/before.js @@ -0,0 +1,3 @@ +import cloneRegexp from 'clone-regexp'; + +const clone1 = cloneRegexp(); diff --git a/test/fixtures/clone-regexp/case-3/result.js b/test/fixtures/clone-regexp/case-3/result.js new file mode 100644 index 0000000..3281073 --- /dev/null +++ b/test/fixtures/clone-regexp/case-3/result.js @@ -0,0 +1 @@ +const clone1 = new RegExp(); diff --git a/test/fixtures/clone-regexp/case-4/after.js b/test/fixtures/clone-regexp/case-4/after.js new file mode 100644 index 0000000..e56a500 --- /dev/null +++ b/test/fixtures/clone-regexp/case-4/after.js @@ -0,0 +1 @@ +const clone1 = new RegExp(); \ No newline at end of file diff --git a/test/fixtures/clone-regexp/case-4/before.js b/test/fixtures/clone-regexp/case-4/before.js new file mode 100644 index 0000000..88d36f6 --- /dev/null +++ b/test/fixtures/clone-regexp/case-4/before.js @@ -0,0 +1,3 @@ +import myCloneRegexp from 'clone-regexp'; + +const clone1 = myCloneRegexp(); \ No newline at end of file diff --git a/test/fixtures/clone-regexp/case-4/result.js b/test/fixtures/clone-regexp/case-4/result.js new file mode 100644 index 0000000..e56a500 --- /dev/null +++ b/test/fixtures/clone-regexp/case-4/result.js @@ -0,0 +1 @@ +const clone1 = new RegExp(); \ No newline at end of file From 550fb65aa20627c210ebf55b1bb1e28ee651e909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Filip?= Date: Sun, 28 Jul 2024 23:31:48 +0200 Subject: [PATCH 2/3] Add warning and refactor code --- codemods/clone-regexp/index.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/codemods/clone-regexp/index.js b/codemods/clone-regexp/index.js index 7d118b7..56036c6 100644 --- a/codemods/clone-regexp/index.js +++ b/codemods/clone-regexp/index.js @@ -28,19 +28,15 @@ export default function (options) { }) .replaceWith((path) => { const args = path.node.arguments; - if (args.length === 0) { - return j.newExpression(j.identifier('RegExp'), []); - } - if (args.length === 1) { - return j.newExpression(j.identifier('RegExp'), [args[0]]); - } + const arg = args.length >= 1 ? [args[0]] : []; + const newRegExp = j.newExpression(j.identifier('RegExp'), arg); if (args.length === 2) { - const newExpression = j.newExpression(j.identifier('RegExp'), [ - args[0], - ]); - newExpression.comments = [j.commentBlock(' Todo ', false, true)]; - return newExpression; + console.warn( + '[WARNING] Options are being passed to `clone-regexp`. Please modify the new regular expression accordingly.', + ); + newRegExp.comments = [j.commentBlock(' Todo ', false, true)]; } + return newRegExp; }) .toSource({ quote: 'single' }); }, From 385438ef3dc949ce481a12bc03ab5dc45f9ff4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Filip?= Date: Sun, 28 Jul 2024 23:39:07 +0200 Subject: [PATCH 3/3] Handle cases for more than 2 arguments --- codemods/clone-regexp/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codemods/clone-regexp/index.js b/codemods/clone-regexp/index.js index 56036c6..3843d5c 100644 --- a/codemods/clone-regexp/index.js +++ b/codemods/clone-regexp/index.js @@ -30,7 +30,7 @@ export default function (options) { const args = path.node.arguments; const arg = args.length >= 1 ? [args[0]] : []; const newRegExp = j.newExpression(j.identifier('RegExp'), arg); - if (args.length === 2) { + if (args.length >= 2) { console.warn( '[WARNING] Options are being passed to `clone-regexp`. Please modify the new regular expression accordingly.', );