From 93cdc7b05f3c63bb6b18682ec46d8a446cfb563c Mon Sep 17 00:00:00 2001 From: Jack Steinberg Date: Wed, 21 Aug 2019 13:57:45 -0400 Subject: [PATCH] Refactor for clarity and extensibility (#45) --- lib/imports.js | 56 +++++++++++++++++++++++--------- lib/mods/clean-console.js | 11 +++++-- lib/mods/replace-constructors.js | 1 + lib/mods/replace-get-set.js | 18 +++++----- lib/mods/replace-methods.js | 52 +++++++++++++++-------------- 5 files changed, 86 insertions(+), 52 deletions(-) diff --git a/lib/imports.js b/lib/imports.js index 4fa14ec..a64967e 100644 --- a/lib/imports.js +++ b/lib/imports.js @@ -1,18 +1,42 @@ 'use strict'; -const {getOriginal} = require('./originals-helper.js'); +const { + getOriginal, + formatReplacement, +} = require('./originals-helper.js'); + +/** + * Replaces the node in question with generated text using the + * original, and updates imports to get those originals. + * + * @param {Node} node - the node to replace + * @param {string} name - the name of the identifier to be replaced + * @param {string} type - the type of replacement + * @param {string} moduleName - the module origin of the original + * @param {string} base - the base on which the action is occurring + * @param {string} args - a string of arguments for the replacement + * @param {Object} imports - the accumulated list of imports + * + * @return {Node} the replaced AST node + */ +function replaceNodeAndUpdateImports(node, name, type, + moduleName, base, args, imports) { + const original = getOriginal(name, type, moduleName); + updateImports(imports, moduleName, name, type); + return node.replaceWithText(formatReplacement(base, original, args, type)); +} /** * Formats the information into the right format for import * * @param {string} identifier - the name of the method / function to get * @param {string} type - the type of original of the identifier - * @param {string} mod - the name of the module to import from + * @param {string} moduleName - the name of the module to import from * * @return {string} the formatted import text */ -function generateImportText(identifier, type, mod) { - const original = getOriginal(identifier, type, mod); +function generateImportText(identifier, type, moduleName) { + const original = getOriginal(identifier, type, moduleName); switch (type) { case 'constructor': return original; @@ -21,11 +45,11 @@ function generateImportText(identifier, type, mod) { case 'method': return `${identifier} as ${original}`; case 'static': - return `${identifier}_static as ${mod}_${identifier}_static`; + return `${identifier}_static as ${moduleName}_${identifier}_static`; case 'get': - return `${identifier}_get as ${mod}_${identifier}_get`; + return `${identifier}_get as ${moduleName}_${identifier}_get`; case 'set': - return `${identifier}_set as ${mod}_${identifier}_set`; + return `${identifier}_set as ${moduleName}_${identifier}_set`; default: throw new Error('not yet implemented'); } @@ -35,22 +59,22 @@ function generateImportText(identifier, type, mod) { * Adds an import for the module to the list * * @param {Object} imports - the current list of imports - * @param {string} mod - the name of the module to import from + * @param {string} moduleName - the name of the module to import from * @param {string} identifier - the name of the method / function to get * @param {string} type - the type of original of the identifier */ -function updateImports(imports, mod, identifier, type) { - const importText = generateImportText(identifier, type, mod); +function updateImports(imports, moduleName, identifier, type) { + const importText = generateImportText(identifier, type, moduleName); - if (!(mod in imports)) { - imports[mod] = { + if (!(moduleName in imports)) { + imports[moduleName] = { namedImports: new Set(), }; } switch (type) { case 'constructor': - imports[mod].defaultImport = importText; + imports[moduleName].defaultImport = importText; break; case 'get': case 'set': @@ -58,13 +82,12 @@ function updateImports(imports, mod, identifier, type) { if (!imports.Reflect) { imports.Reflect = {namedImports: new Set(['apply as Reflect_apply'])}; } - imports.Reflect.namedImports.add('apply as Reflect_apply'); - imports[mod].namedImports.add(importText); + imports[moduleName].namedImports.add(importText); break; case 'window': case 'namespace': case 'static': - imports[mod].namedImports.add(importText); + imports[moduleName].namedImports.add(importText); break; default: throw new Error('not yet implemented'); @@ -91,4 +114,5 @@ function addAllImports(sourceFile, imports) { module.exports = { updateImports, addAllImports, + replaceNodeAndUpdateImports, }; diff --git a/lib/mods/clean-console.js b/lib/mods/clean-console.js index 79e003e..1054897 100644 --- a/lib/mods/clean-console.js +++ b/lib/mods/clean-console.js @@ -1,6 +1,9 @@ 'use strict'; const nodeTypes = require('ts-morph').TypeGuards; +const { + isGlobal, +} = require('../originals-helper.js'); /** * Checks if a given node is a ConsoleCallExpression @@ -11,12 +14,16 @@ const nodeTypes = require('ts-morph').TypeGuards; */ function isConsoleCallExpression(node) { if (!nodeTypes.isCallExpression(node)) { - return; + return false; } const propertyAccess = node.getExpression(); if (!nodeTypes.isPropertyAccessExpression(propertyAccess)) { - return; + return false; + } + + if (!isGlobal(propertyAccess)) { + return false; } const base = propertyAccess.getExpression(); diff --git a/lib/mods/replace-constructors.js b/lib/mods/replace-constructors.js index 7d802b2..7c57feb 100644 --- a/lib/mods/replace-constructors.js +++ b/lib/mods/replace-constructors.js @@ -19,6 +19,7 @@ function replaceGlobalConstructors(node, imports) { const identifier = node.getExpression(); const mod = node.getType().getSymbol().getName(); const original = getOriginal(mod, 'constructor', mod); + identifier.replaceWithText(original); updateImports(imports, mod, original, 'constructor'); } diff --git a/lib/mods/replace-get-set.js b/lib/mods/replace-get-set.js index dfe11ff..34e5b82 100644 --- a/lib/mods/replace-get-set.js +++ b/lib/mods/replace-get-set.js @@ -1,11 +1,11 @@ 'use strict'; const nodeTypes = require('ts-morph').TypeGuards; -const {updateImports} = require('../imports.js'); const { - formatReplacement, + replaceNodeAndUpdateImports, +} = require('../imports.js'); +const { isGlobal, - getOriginal, } = require('../originals-helper.js'); const dataProperties = new Map([ @@ -43,8 +43,7 @@ function isGlobalGet(node) { function isGlobalSet(node) { return nodeTypes.isBinaryExpression(node) && node.getOperatorToken().getText() === '=' && - nodeTypes.isPropertyAccessExpression(node.getLeft()) && - isGlobal(node.getLeft().getExpression()); + isGlobalGet(node.getLeft()); } /** @@ -54,7 +53,9 @@ function isGlobalSet(node) { * @param {Object} imports - the current list of imports */ function replaceGlobalGetSetCalls(node, imports) { - let args; let type; let propertyAccessNode; + let args; + let type; + let propertyAccessNode; if (isGlobalGet(node)) { propertyAccessNode = node; @@ -76,10 +77,9 @@ function replaceGlobalGetSetCalls(node, imports) { } const baseObject = propertyAccessNode.getExpression().getText(); - const original = getOriginal(propertyName, type, moduleName); - node.replaceWithText(formatReplacement(baseObject, original, args, type)); - updateImports(imports, moduleName, propertyName, type); + replaceNodeAndUpdateImports(node, propertyName, type, + moduleName, baseObject, args, imports); } module.exports = replaceGlobalGetSetCalls; diff --git a/lib/mods/replace-methods.js b/lib/mods/replace-methods.js index 91d65ed..e99ad60 100644 --- a/lib/mods/replace-methods.js +++ b/lib/mods/replace-methods.js @@ -1,11 +1,11 @@ 'use strict'; const nodeTypes = require('ts-morph').TypeGuards; -const {updateImports} = require('../imports.js'); const { - formatReplacement, + replaceNodeAndUpdateImports, +} = require('../imports.js'); +const { isGlobal, - getOriginal, } = require('../originals-helper.js'); /** @@ -21,7 +21,7 @@ function isNamespaceMethod(node, baseTypeName) { /** * If the first check passes, check the name of the first declaration - * of the callee, to see if it is the same as the name of the type. + * of the baseObject, to see if it is the same as the name of the type. * This approach covers aliasing: * * e.g. @@ -31,18 +31,18 @@ function isNamespaceMethod(node, baseTypeName) { * First declared as CSS, with a baseTypeName of CSS (Namespace ✔) */ if (firstDeclaration && firstDeclaration.getInitializer) { - const callee = firstDeclaration.getInitializer().getText(); - return callee === baseTypeName; + const baseObject = firstDeclaration.getInitializer().getText(); + return baseObject === baseTypeName; } /** * If you can't get the initializer from the firstDeclaration, - * do a shallow comparison of the callee name and the baseTypeName. + * do a shallow comparison of the baseObject name and the baseTypeName. * * e.g. * Math.max(); * - * The callee name Math is the same as the baseTypeName Math (Namespace ✔) + * The baseObject name Math is the same as the baseTypeName Math (Namespace ✔) */ return symbol.getName() === baseTypeName; } @@ -65,42 +65,44 @@ function getArgumentString(functionCall) { function replaceGlobalMethodCalls(node, imports) { if (nodeTypes.isCallExpression(node) && isGlobal(node.getExpression())) { - let mod; let args; let type; let callee; + let moduleName; + let type; + let baseObject; + let propertyName; // property access methods if (nodeTypes.isPropertyAccessExpression(node.getExpression())) { const propertyAccess = node.getExpression(); - name = propertyAccess.getSymbol().getEscapedName(); + propertyName = propertyAccess.getName(); type = 'method'; let baseType = propertyAccess.getExpression().getType(); - const baseConstructSignature = baseType.getConstructSignatures()[0]; + // the method is static if the base has a constructSignature + // (i.e. the base is a constructor) + const baseConstructSignature = baseType.getConstructSignatures()[0]; if (baseConstructSignature) { - baseType = baseConstructSignature.getReturnType(); type = 'static'; + baseType = baseConstructSignature.getReturnType(); } - mod = baseType.getSymbol().getEscapedName(); - args = getArgumentString(propertyAccess.getParent()); - callee = propertyAccess.getExpression().getSymbol().getName(); + moduleName = baseType.getSymbol().getName(); + baseObject = propertyAccess.getExpression().getSymbol().getName(); - if (type !== 'static' && isNamespaceMethod(propertyAccess, mod)) { + if (type !== 'static' && isNamespaceMethod(propertyAccess, moduleName)) { type = 'namespace'; - callee = mod; + baseObject = moduleName; } } else { // window functions type = 'window'; - mod = 'Window'; - name = node.getExpression().getSymbol().getEscapedName(); - callee = mod; - - args = getArgumentString(node); + moduleName = 'Window'; + propertyName = node.getExpression().getText(); + baseObject = moduleName; } - const original = getOriginal(name, type, mod); - node.replaceWithText(formatReplacement(callee, original, args, type)); - updateImports(imports, mod, name, type); + const args = getArgumentString(node); + replaceNodeAndUpdateImports(node, propertyName, type, moduleName, + baseObject, args, imports); } }