Last active
November 26, 2020 04:01
-
-
Save pastak/228c5fa05315e4e50c65605826d0c138 to your computer and use it in GitHub Desktop.
Change import style `import foo from 'lodash/foo'` from `import _ from 'lodash'` or `import {add} from 'lodash'` for Tree shaking
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const MUST_ADD_PREFIX = false; | |
const PREFIX = "_"; | |
const sameProperties = (props, a, b) => { | |
return props.every((p) => a[p] === b[p]); | |
}; | |
const sameLocation = (loc1, loc2) => sameProperties(["line", "column", "token"], loc1.start, loc2.start) && sameProperties(["line", "column", "token"], loc1.end, loc2.end); | |
const findBinding = (path, name) => { | |
const namedBind = path.scope.getBindings()[name]; | |
if (namedBind) return namedBind[0]; | |
if (!path.parent) return; | |
return findBinding(path.parent, name); | |
}; | |
/* https://github.com/cpojer/js-codemod/blob/master/transforms/underscore-to-lodash-native.js */ | |
const NATIVE_METHODS = { | |
forEach: "forEach", | |
each: "forEach", | |
map: "map", | |
collect: "map", | |
filter: "filter", | |
select: "filter", | |
every: "every", | |
some: "some", | |
find: "find", | |
detect: "find", | |
contains: "includes", | |
reduce: "reduce", | |
inject: "reduce", | |
indexOf: "indexOf", | |
lastIndexOf: "lastIndexOf", | |
first: (j, identifier) => j.memberExpression(identifier, j.literal(0)), | |
last: (j, identifier) => j.memberExpression(identifier, j.binaryExpression("-", j.memberExpression(identifier, j.identifier("length")), j.literal(1))) | |
}; | |
function transformExpression(j, options) { | |
return (ast) => { | |
const methodName = ast.node.callee.property.name; | |
const nativeMapping = NATIVE_METHODS[methodName]; | |
if (nativeMapping) { | |
if (typeof nativeMapping === "function") { | |
transformNativeSpecial(j, ast); | |
} else { | |
transformNativeMethod(j, ast); | |
} | |
} | |
}; | |
} | |
function transformNativeSpecial(j, ast) { | |
const methodName = ast.node.callee.property.name; | |
const nativeMapping = NATIVE_METHODS[methodName]; | |
j(ast).replaceWith(nativeMapping(j, ast.node.arguments[0])); | |
} | |
function transformNativeMethod(j, ast) { | |
const methodName = ast.node.callee.property.name; | |
const nativeMapping = NATIVE_METHODS[methodName]; | |
const arg = ast.node.arguments.slice(1).map((a) => (a.type === "StringLiteral" ? j.arrowFunctionExpression( | |
[j.identifier("t")], | |
j.identifier(`t.${a.value}`) | |
) : a)); | |
j(ast).replaceWith(j.callExpression(j.memberExpression(ast.node.arguments[0], j.identifier(nativeMapping)), arg)); | |
} | |
const isUnderscoreExpression = (name) => (node) => { | |
return node.type === "CallExpression" && node.callee.type === "MemberExpression" && node.callee.object && node.callee.object.name === name; | |
}; | |
/* https://github.com/cpojer/js-codemod/blob/master/transforms/underscore-to-lodash-native.js */ | |
export default function transformer(file, api) { | |
const j = api.jscodeshift; | |
j.__methods = {}; | |
const root = j(file.source); | |
let lodashCallExpPaths = []; | |
let defaultImportPath; | |
let removeImportPaths = []; | |
const importMap = new Map(); | |
root.find(j.ImportDeclaration).forEach((_importDeclarationPath) => { | |
if (_importDeclarationPath.value.source.value !== "lodash") return; | |
const isDefaultImport = _importDeclarationPath.value.specifiers.find((s) => s.type === "ImportDefaultSpecifier"); | |
if (isDefaultImport) { | |
defaultImportPath = _importDeclarationPath; | |
} | |
if (!(isDefaultImport && root.find(j.CallExpression, { callee: { name: _importDeclarationPath.node.specifiers[0].local.name } }).length > 0)) { | |
removeImportPaths.push(_importDeclarationPath); | |
} | |
const { specifiers } = _importDeclarationPath.node; | |
specifiers.forEach((specifier) => { | |
const assignedLocalName = specifier.local.name; | |
if (specifier.imported) { | |
importMap.set(assignedLocalName, specifier.imported.name); | |
} else { | |
if (!isDefaultImport) importMap.set(assignedLocalName, assignedLocalName); | |
} | |
if (isDefaultImport) root.find(j.CallExpression, isUnderscoreExpression(assignedLocalName)).forEach(transformExpression(j)); | |
const callExps = j(file.source).find(j.CallExpression); | |
const callExpsCalleMaybeLodashIndexes = callExps | |
.nodes() | |
.map((node, index) => | |
isDefaultImport | |
? node.callee.object && node.callee.object.type === "Identifier" && node.callee.object.name === assignedLocalName && index | |
: node.callee.type === "Identifier" && node.callee.name === assignedLocalName && index | |
) | |
.filter((_) => _ !== false); | |
const lodashCallExpsIndexes = callExps | |
.paths() | |
.map((path, index) => { | |
if (!callExpsCalleMaybeLodashIndexes.includes(index)) return false; | |
const binding = findBinding(path, assignedLocalName); | |
return binding && sameLocation(binding.node.loc, specifier.loc) && index; | |
}) | |
.filter((_) => typeof _ === "number"); | |
callExps | |
.filter((_, index) => lodashCallExpsIndexes.includes(index)) | |
.paths() | |
.forEach((p) => lodashCallExpPaths.push(p)); | |
}); | |
}); | |
const nameMap = new Map(); | |
root | |
.find(j.CallExpression) | |
.filter((callExpr) => { | |
return lodashCallExpPaths.some((lodashCallExpPath) => callExpr.node.loc && sameLocation(lodashCallExpPath.node.loc, callExpr.node.loc)); | |
}) | |
.replaceWith((path) => { | |
const node = path.value; | |
const propertyName = node.callee.name || node.callee.property.name; | |
if (!nameMap.has(propertyName)) { | |
const isConfused = !!root.findVariableDeclarators(propertyName).length; | |
const newName = MUST_ADD_PREFIX || isConfused ? `${PREFIX}${propertyName}` : propertyName; | |
nameMap.set(propertyName, newName); | |
} | |
const newName = nameMap.get(propertyName); | |
const newNode = j.callExpression(j.identifier(newName), node.arguments); | |
return newNode; | |
}); | |
importMap.forEach((local, imported) => { | |
nameMap.set(local, imported); | |
}); | |
if (removeImportPaths.length > 0) { | |
nameMap.forEach((newName, propertyName) => { | |
removeImportPaths[0].insertAfter(`import ${newName} from "lodash/${propertyName}";`); | |
}); | |
removeImportPaths.forEach((p) => j(p).remove()); | |
} | |
return root.toSource(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import _ from "lodash" | |
import {add, diff, sum as execSum, aaa as bbb} from "lodash" | |
_.bar(); | |
_.hage(); | |
_.foo(); | |
bbb() | |
_.each([1,2], (a => _.foo(a))) | |
(() => { | |
const _ = document | |
_.getElementById() | |
}); | |
(() => { | |
_.hoge() | |
const add = () => {} | |
add() | |
}); | |
add(); | |
const assignedDiff = diff | |
window.sum = execSum; | |
document.getElementById() | |
_.map(arr, 'foo') | |
_.filter(arr, 'bar') | |
_.map(arr, p => p.foo) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bbb from "lodash/aaa"; | |
import execSum from "lodash/sum"; | |
import diff from "lodash/diff"; | |
import add from "lodash/add"; | |
import hoge from "lodash/hoge"; | |
import foo from "lodash/foo"; | |
import hage from "lodash/hage"; | |
import bar from "lodash/bar"; | |
bar(); | |
hage(); | |
foo(); | |
bbb() | |
[1,2].forEach(a => foo(a)) | |
(() => { | |
const _ = document | |
_.getElementById() | |
}); | |
(() => { | |
hoge() | |
const add = () => {} | |
add() | |
}); | |
_add(); | |
const assignedDiff = diff | |
window.sum = execSum; | |
document.getElementById() | |
arr.map(t => t.foo) | |
arr.filter(t => t.bar) | |
arr.map(p => p.foo) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://github.com/cpojer/js-codemod#underscore-to-lodash-native