From fc6062969da8893e48e2db7b6274de1965ed6286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Filip?= Date: Fri, 2 Aug 2024 21:00:44 +0200 Subject: [PATCH] Add codemod for globalthis --- codemods/globalthis/index.js | 37 ++++++++ codemods/shared.js | 109 ++++++++++++++++++++-- index.js | 4 +- test/fixtures/globalthis/case-1/after.js | 1 + test/fixtures/globalthis/case-1/before.js | 2 + test/fixtures/globalthis/case-1/result.js | 1 + test/fixtures/globalthis/case-2/after.js | 1 + test/fixtures/globalthis/case-2/before.js | 2 + test/fixtures/globalthis/case-2/result.js | 1 + test/fixtures/globalthis/case-3/after.js | 1 + test/fixtures/globalthis/case-3/before.js | 2 + test/fixtures/globalthis/case-3/result.js | 1 + test/fixtures/globalthis/case-4/after.js | 1 + test/fixtures/globalthis/case-4/before.js | 2 + test/fixtures/globalthis/case-4/result.js | 1 + test/fixtures/globalthis/case-5/after.js | 1 + test/fixtures/globalthis/case-5/before.js | 2 + test/fixtures/globalthis/case-5/result.js | 1 + 18 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 codemods/globalthis/index.js create mode 100644 test/fixtures/globalthis/case-1/after.js create mode 100644 test/fixtures/globalthis/case-1/before.js create mode 100644 test/fixtures/globalthis/case-1/result.js create mode 100644 test/fixtures/globalthis/case-2/after.js create mode 100644 test/fixtures/globalthis/case-2/before.js create mode 100644 test/fixtures/globalthis/case-2/result.js create mode 100644 test/fixtures/globalthis/case-3/after.js create mode 100644 test/fixtures/globalthis/case-3/before.js create mode 100644 test/fixtures/globalthis/case-3/result.js create mode 100644 test/fixtures/globalthis/case-4/after.js create mode 100644 test/fixtures/globalthis/case-4/before.js create mode 100644 test/fixtures/globalthis/case-4/result.js create mode 100644 test/fixtures/globalthis/case-5/after.js create mode 100644 test/fixtures/globalthis/case-5/before.js create mode 100644 test/fixtures/globalthis/case-5/result.js diff --git a/codemods/globalthis/index.js b/codemods/globalthis/index.js new file mode 100644 index 0000000..e921940 --- /dev/null +++ b/codemods/globalthis/index.js @@ -0,0 +1,37 @@ +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: 'globalthis', + transform: ({ file }) => { + const j = jscodeshift; + const root = j(file.source); + const identifier1 = removeImport('globalthis', root, j).identifier; + const identifier2 = removeImport( + 'globalthis/polyfill', + root, + j, + ).identifier; + const identifier3 = removeImport('globalthis/shim', root, j).identifier; + const identifier = identifier1 || identifier2 || identifier3; + + if (identifier) { + root + .find(j.Identifier, { name: identifier }) + .replaceWith(j.identifier('globalThis')); + } + + return root.toSource({ quote: 'single' }); + }, + }; +} diff --git a/codemods/shared.js b/codemods/shared.js index d85b48f..11007ae 100644 --- a/codemods/shared.js +++ b/codemods/shared.js @@ -33,6 +33,91 @@ export function removeImport(name, root, j) { }, }); + // Require statements with call expressions like `var globalThis = require('globalthis')()` + const requireCallExpression = root.find(j.VariableDeclarator, { + init: { + callee: { + type: 'CallExpression', + callee: { + name: 'require', + }, + arguments: [ + { + value: name, + }, + ], + }, + }, + }); + + // Same as above without variable declaration like `require('globalthis')()` + const sideEffectRequireCallExpression = root.find(j.ExpressionStatement, { + expression: { + callee: { + type: 'CallExpression', + callee: { + name: 'require', + }, + arguments: [ + { + value: name, + }, + ], + }, + }, + }); + + // Require chained call expressions like `var globalThis = require('globalthis').shim()` + const requireMethodCallExpression = root.find(j.VariableDeclarator, { + init: { + type: 'CallExpression', + callee: { + type: 'MemberExpression', + object: { + type: 'CallExpression', + callee: { + name: 'require', + }, + arguments: [ + { + value: name, + }, + ], + }, + property: { + type: 'Identifier', + }, + }, + }, + }); + + // Same as above without variable declaration like `require('globalthis').shim()` + const sideEffectRequireMethodCallExpression = root.find( + j.ExpressionStatement, + { + expression: { + type: 'CallExpression', + callee: { + type: 'MemberExpression', + object: { + type: 'CallExpression', + callee: { + name: 'require', + }, + arguments: [ + { + value: name, + }, + ], + }, + property: { + type: 'Identifier', + }, + }, + }, + }, + ); + // Require statements without declarations like `Object.is = require("object-is");` const requireAssignment = root.find(j.AssignmentExpression, { operator: '=', @@ -64,17 +149,25 @@ export function removeImport(name, root, j) { // Return the identifier name, e.g. 'fn' in `import { fn } from 'is-boolean-object'` // or `var fn = require('is-boolean-object')` - const identifier = - importDeclaration.paths().length > 0 - ? importDeclaration.get().node.specifiers[0].local.name - : requireDeclaration.paths().length > 0 - ? requireDeclaration.find(j.Identifier).get().node.name - : requireAssignment.paths().length > 0 - ? requireAssignment.find(j.Identifier).get().node.name - : null; + let identifier = null; + if (importDeclaration.paths().length > 0) { + identifier = importDeclaration.get().node.specifiers[0].local.name; + } else if (requireDeclaration.paths().length > 0) { + identifier = requireDeclaration.find(j.Identifier).get().node.name; + } else if (requireCallExpression.paths().length > 0) { + identifier = requireCallExpression.find(j.Identifier).get().node.name; + } else if (requireMethodCallExpression.paths().length > 0) { + identifier = requireMethodCallExpression.find(j.Identifier).get().node.name; + } else if (requireAssignment.paths().length > 0) { + identifier = requireAssignment.find(j.Identifier).get().node.name; + } importDeclaration.remove(); requireDeclaration.remove(); + requireCallExpression.remove(); + sideEffectRequireCallExpression.remove(); + requireMethodCallExpression.remove(); + sideEffectRequireMethodCallExpression.remove(); requireAssignment.remove(); sideEffectRequireExpression.remove(); diff --git a/index.js b/index.js index f72f54b..37bda5a 100644 --- a/index.js +++ b/index.js @@ -58,6 +58,7 @@ import functionBind from './codemods/function-bind/index.js'; import functionPrototypeName from './codemods/function.prototype.name/index.js'; import functionsHaveNames from './codemods/functions-have-names/index.js'; import getSymbolDescription from './codemods/get-symbol-description/index.js'; +import globalthis from './codemods/globalthis/index.js'; import gopd from './codemods/gopd/index.js'; import has from './codemods/has/index.js'; import hasOwnProp from './codemods/has-own-prop/index.js'; @@ -210,6 +211,7 @@ export const codemods = { "function.prototype.name": functionPrototypeName, "functions-have-names": functionsHaveNames, "get-symbol-description": getSymbolDescription, + "globalthis": globalthis, "gopd": gopd, "has": has, "has-own-prop": hasOwnProp, @@ -300,4 +302,4 @@ export const codemods = { "typed-array-length": typedArrayLength, "typedarray.prototype.slice": typedarrayPrototypeSlice, "xtend": xtend, -}; +}; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-1/after.js b/test/fixtures/globalthis/case-1/after.js new file mode 100644 index 0000000..55fb27b --- /dev/null +++ b/test/fixtures/globalthis/case-1/after.js @@ -0,0 +1 @@ +globalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-1/before.js b/test/fixtures/globalthis/case-1/before.js new file mode 100644 index 0000000..44121d3 --- /dev/null +++ b/test/fixtures/globalthis/case-1/before.js @@ -0,0 +1,2 @@ +var globalThis = require('globalthis')(); +globalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-1/result.js b/test/fixtures/globalthis/case-1/result.js new file mode 100644 index 0000000..55fb27b --- /dev/null +++ b/test/fixtures/globalthis/case-1/result.js @@ -0,0 +1 @@ +globalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-2/after.js b/test/fixtures/globalthis/case-2/after.js new file mode 100644 index 0000000..55fb27b --- /dev/null +++ b/test/fixtures/globalthis/case-2/after.js @@ -0,0 +1 @@ +globalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-2/before.js b/test/fixtures/globalthis/case-2/before.js new file mode 100644 index 0000000..035c517 --- /dev/null +++ b/test/fixtures/globalthis/case-2/before.js @@ -0,0 +1,2 @@ +var globalThis = require('globalthis/polyfill')(); +globalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-2/result.js b/test/fixtures/globalthis/case-2/result.js new file mode 100644 index 0000000..55fb27b --- /dev/null +++ b/test/fixtures/globalthis/case-2/result.js @@ -0,0 +1 @@ +globalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-3/after.js b/test/fixtures/globalthis/case-3/after.js new file mode 100644 index 0000000..55fb27b --- /dev/null +++ b/test/fixtures/globalthis/case-3/after.js @@ -0,0 +1 @@ +globalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-3/before.js b/test/fixtures/globalthis/case-3/before.js new file mode 100644 index 0000000..5934ec4 --- /dev/null +++ b/test/fixtures/globalthis/case-3/before.js @@ -0,0 +1,2 @@ +var newGlobalThis = require('globalthis')(); +newGlobalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-3/result.js b/test/fixtures/globalthis/case-3/result.js new file mode 100644 index 0000000..55fb27b --- /dev/null +++ b/test/fixtures/globalthis/case-3/result.js @@ -0,0 +1 @@ +globalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-4/after.js b/test/fixtures/globalthis/case-4/after.js new file mode 100644 index 0000000..55fb27b --- /dev/null +++ b/test/fixtures/globalthis/case-4/after.js @@ -0,0 +1 @@ +globalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-4/before.js b/test/fixtures/globalthis/case-4/before.js new file mode 100644 index 0000000..3e97f57 --- /dev/null +++ b/test/fixtures/globalthis/case-4/before.js @@ -0,0 +1,2 @@ +var shimmedGlobal = require('globalthis').shim(); +shimmedGlobal.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-4/result.js b/test/fixtures/globalthis/case-4/result.js new file mode 100644 index 0000000..55fb27b --- /dev/null +++ b/test/fixtures/globalthis/case-4/result.js @@ -0,0 +1 @@ +globalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-5/after.js b/test/fixtures/globalthis/case-5/after.js new file mode 100644 index 0000000..55fb27b --- /dev/null +++ b/test/fixtures/globalthis/case-5/after.js @@ -0,0 +1 @@ +globalThis.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-5/before.js b/test/fixtures/globalthis/case-5/before.js new file mode 100644 index 0000000..9ca3caf --- /dev/null +++ b/test/fixtures/globalthis/case-5/before.js @@ -0,0 +1,2 @@ +var shimmedGlobal = require('globalthis/shim')(); +shimmedGlobal.a = 42; \ No newline at end of file diff --git a/test/fixtures/globalthis/case-5/result.js b/test/fixtures/globalthis/case-5/result.js new file mode 100644 index 0000000..55fb27b --- /dev/null +++ b/test/fixtures/globalthis/case-5/result.js @@ -0,0 +1 @@ +globalThis.a = 42; \ No newline at end of file