Last active
February 9, 2022 12:11
-
-
Save drFabio/becb947ab3b37f53fc30eda563c8b8ae to your computer and use it in GitHub Desktop.
Adds props to objects using jsCodeShift
This file contains 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
export default (fileInfo, api, options) => { | |
const { path: rawPath, value, declarator = "theme" } = options; | |
if (!rawPath || !value) { | |
console.error("Pass paths AND value to change the code "); | |
process.exit(1); | |
} | |
const paths = rawPath.split("."); | |
const j = api.jscodeshift; | |
const root = j(fileInfo.source); | |
const possibleRoots = root.find(j.VariableDeclarator, { | |
id: { name: declarator }, | |
}); | |
const rootNode = possibleRoots.filter((node) => { | |
return node.parentPath.get(0).value.type === "VariableDeclarator"; | |
}); | |
if (!rootNode.length && possibleRoots.length) { | |
console.warn( | |
`${declarator} was found but none had the VariableDeclarator so we ruled it out i.e const ${declarator} = {}` | |
); | |
process.exit(1); | |
} | |
// If we can't find the initial declarator then there is no point on proceeding | |
if (!rootNode.length) { | |
console.error( | |
`${declarator} not found. we couldn't find a variable with it's value` | |
); | |
process.exit(1); | |
} | |
let current = rootNode; | |
let found = false; | |
let enteredRootDeclarator = false; | |
// try to find as deep as a path as we can | |
do { | |
const name = paths[0]; | |
const newCollection = current | |
.find(j.Property, { key: { name } }) | |
.filter((obj, index) => { | |
return isDirectChildOfObject(current.get(0).node, obj); | |
}); | |
if (!newCollection.length) { | |
found = false; | |
// at this point we found a partial match so we will continue the rest | |
console.log(`Not found we will enrich adding ${paths.join(".")}`); | |
continue; | |
} | |
found = true; | |
current = newCollection; | |
enteredRootDeclarator = true; | |
paths.shift(); | |
} while (found && paths.length); | |
// we never entered the path we need to get the properties of the initial object. | |
// this means the value is totally new | |
if (!enteredRootDeclarator) { | |
current = rootNode.find(j.ObjectExpression).filter((obj, index) => { | |
return index === 0; | |
}); | |
} | |
current.replaceWith((nodePath) => { | |
const { node } = nodePath; | |
const { length } = paths; | |
if (!length) { | |
//We have an exact match, need to replace it | |
node.value = j.literal(value); | |
return node; | |
} | |
// From here we need to traverse the remainder of the paths and add it until we can add the literal | |
let previous = enteredRootDeclarator ? node.value : node; | |
// here we are adding the remainder of not found paths | |
paths.forEach((remainder, index) => { | |
const isLast = length - 1 === index; | |
const object = j.property( | |
"init", | |
j.identifier(remainder), | |
isLast ? j.literal(value) : j.objectExpression([]) | |
); | |
previous.properties.push(object); | |
previous = object.value; | |
}); | |
return node; | |
}); | |
return root.toSource(); | |
}; | |
function isDirectChildOfObject(parent, child) { | |
let current = child.parentPath; | |
// Ignoring object expressions so we can find nested object | |
while (current.node && current.node.type === "ObjectExpression") { | |
current = current.parentPath; | |
} | |
return current.node === parent; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A JS Codeshift codemod
Given a file like:
You can execute the following command: (-d for dry run, -p for print)
This will add hotDog with value of 10 to food
This will add a new gin object inside alcoholic with a value of {beefEater:5}
That will add a new category called desserts with an object cakes, with a value of choclate:3.
PS: Remember the amazing AST Explorer to figure out what to shift
For a good tutorial check the toptal one