react-hookify
Version:
A CLI tool to convert React class components into functional components with hooks
191 lines (189 loc) • 6.36 kB
JavaScript
/* eslint-disable no-lonely-if */
/* eslint-disable guard-for-in */
/* eslint-disable complexity */
/* eslint-disable max-statements */
const {
getInsideOfFunc,
getEndIdxOfBraces,
getEndIdxOfFunc,
} = require('../commonUtils')
/*
INPUT:
let obj = {
componentDidMount: useEffect(() => {
statement1
statement2
cleanup1
}),
componentDidUpdate: useEffect(() => {
statement1
statement2
etc
}),
componentWillUnmount: useEffect(() => {
cleanup2
}),
}
OUTPUT
[
useEffect(() => {
statement1
cleanup1
}),
useEffect(() => {
statement2
}),
]
*/
const iterator = (obj) => {
let effectsArr = []
for (let key in obj) {
if (key === 'componentDidMount()') {
let inside = getInsideOfFunc(obj[key], 'useEffect').trim()
let secondArgIdx = getEndIdxOfFunc(obj[key], 'useEffect') + 1
let argumentSlice = obj[key].slice(secondArgIdx)
let secondArg = argumentSlice.match(/(\[)(.*?)(])/)[0]
let currentEffectsArr = parser(inside, secondArg)
effectsArr = [...effectsArr, ...currentEffectsArr]
} else if (key === 'componentDidUpdate()') {
effectsArr = [...effectsArr, ...obj[key]]
} else {
effectsArr.push(obj[key])
}
}
return effectsArr
}
const parser = (useEffectString, secondArg) => {
let variablesObj = findUniqueVars(useEffectString)
let statementsArr = findStatements(useEffectString)
combineVars(variablesObj, statementsArr)
let groupedStatementsArr = groupStatements(variablesObj, statementsArr)
let useEffectsArr = createUseEffects(groupedStatementsArr, secondArg)
return useEffectsArr
}
function createUseEffects(groupedStatementsArr, secondArg) {
let effectsArr = []
for (let i = 0; i < groupedStatementsArr.length; i++) {
let singleUseEffect = `useEffect(() => {
${groupedStatementsArr[i].join('\n')}
}, ${secondArg})`
effectsArr.push(singleUseEffect)
}
return effectsArr
}
function groupStatements(variablesObj, statementsArr) {
let statementsArrFound = []
let groupedStatementsArr = []
for (let key in variablesObj) {
let currentGroup = []
// going through one variables array of children:
for (let variable of variablesObj[key].variables) {
let regex = new RegExp(`([^a-z0-9])(${variable})([^a-z0-9])`)
for (let i = 0; i < statementsArr.length; i++) {
let statement = statementsArr[i]
if (statement.search(regex) > -1) {
if (!currentGroup.includes(statement)) {
currentGroup.push(statement)
}
if (!statementsArrFound.includes(statement)) {
statementsArrFound.push(statement)
}
}
}
}
if (currentGroup.length) {
groupedStatementsArr.push(currentGroup)
}
currentGroup = []
}
let remainingStatements = statementsArr.filter((statement) => {
return !statementsArrFound.includes(statement)
})
for (let i = 0; i < remainingStatements.length; i++) {
groupedStatementsArr.push([remainingStatements[i]])
}
return groupedStatementsArr
}
function combineVars(variablesObj, statementArr) {
// go through each statement
for (let statement of statementArr) {
let foundVar = false
let firstVar
// find the first unique variable in the statement
// if theres more than one unique variable, update the variables Obj to show that link
for (let variable in variablesObj) {
let regex = new RegExp(`([^a-z0-9])(${variable})([^a-z0-9])?`)
if (foundVar === false) {
if (statement.search(regex) > -1) {
firstVar = variable
foundVar = true
}
} else {
// if theres a second variable in the statement
if (statement.search(regex) > -1) {
// check if first var has a parent. If first var has a parent, then add any additional variables to that parent. If first var has no parent, then define parent relationship between first and second vars now
if (variablesObj[firstVar].parent) {
let papa = variablesObj[firstVar].parent
variablesObj[papa].variables.push(variable)
variablesObj[variable].parent = papa
} else {
variablesObj[firstVar].variables.push(variable)
variablesObj[variable].parent = firstVar
}
}
}
}
}
// delete any children from object (only keep parents and have their children be in their respective values)
for (let key in variablesObj) {
if (variablesObj[key].parent) {
delete variablesObj[key]
}
}
}
function findUniqueVars(useEffectString) {
let variablesObj = {}
let newString = useEffectString
while (newString.search(/([a-z0-9_.[]+)(]?)([^a-z]*)(=)/i) > -1) {
let matchedArr = newString.match(/([a-z0-9_.[]+)(]?)([^a-z]*)(=)/i)
let newVar = matchedArr[1] + matchedArr[2]
// if right side of equal sign matches to an existing variable, then add it to the respective array in the obj
// else make a new key for that var
variablesObj[newVar] = { variables: [newVar], statements: [] }
newString = newString.substring(matchedArr.index + matchedArr[0].length)
}
return variablesObj
}
function findStatements(string) {
// while loop to cut/paste each line/statement into their appropriate effect
let newString = string.trim()
const statementsArr = []
// get statement (based of either \n or valid braces)
while (newString.length) {
let newStatement
if (newString.indexOf('\n') === -1) {
newStatement = newString
statementsArr.push(newStatement)
newString = ''
} else {
// if valid braces is true (or no braces) then we look for newline. If valid braces is false, then we need the index of the close brace
let newLineIdx = newString.indexOf('\n')
newStatement = newString.slice(0, newString.indexOf('\n'))
if (newStatement.search(/[{[(]/) > -1) {
let endIdx = getEndIdxOfBraces(newString)
if (endIdx > newLineIdx) {
if (!newString.indexOf('\n', endIdx) === -1) {
endIdx = newString.indexOf('\n', endIdx)
}
newStatement = newString.slice(0, endIdx)
}
}
newString = newString.substring(newStatement.length + 2) // might need to be +1?
statementsArr.push(newStatement)
}
}
return statementsArr
}
module.exports = {
iterator,
}