Skip to content

Commit

Permalink
Refactor for clarity and extensibility (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack Steinberg authored Aug 21, 2019
1 parent 238457c commit 93cdc7b
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 52 deletions.
56 changes: 40 additions & 16 deletions lib/imports.js
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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');
}
Expand All @@ -35,36 +59,35 @@ 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':
case 'method':
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');
Expand All @@ -91,4 +114,5 @@ function addAllImports(sourceFile, imports) {
module.exports = {
updateImports,
addAllImports,
replaceNodeAndUpdateImports,
};
11 changes: 9 additions & 2 deletions lib/mods/clean-console.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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();
Expand Down
1 change: 1 addition & 0 deletions lib/mods/replace-constructors.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
Expand Down
18 changes: 9 additions & 9 deletions lib/mods/replace-get-set.js
Original file line number Diff line number Diff line change
@@ -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([
Expand Down Expand Up @@ -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());
}

/**
Expand All @@ -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;
Expand All @@ -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;
52 changes: 27 additions & 25 deletions lib/mods/replace-methods.js
Original file line number Diff line number Diff line change
@@ -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');

/**
Expand All @@ -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.
Expand All @@ -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;
}
Expand All @@ -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);
}
}

Expand Down

0 comments on commit 93cdc7b

Please sign in to comment.