Migrating our scripts to Node.js v16 using codemod
At Entria we have a lot of Node.js scripts to automate common tasks
we also make a script for any migration and to make it easy to run a single migration for test purposes and each script starts with anonymous async functions that is auto executed.
Like this template
construn=async()=>{//migration code}(async()=>{try{awaitrun();}catch(err){// eslint-disable-next-lineconsole.log('err:',err);process.exit(1);}process.exit(0);})();
This works well, but not enough for our use case. Because we create tests for our scripts and migrations if they are used in production.
If you import the run function in your test files it will run the async functions in your tests, which is not the behavior you wanna. So we have a check async function that is only auto executed when we are running directly.
To make this check, we usedmodule.parent
propriety, but it will be deprecated on Node v16.
Context of Node v14 and v16
In October 26th, 2021 Node.js v16 replaced v14 as the LTS release.
And with these changes we on Entria bring to us breaking changes into our codebase on Entria, like a module.parent.
module.parent
has used on Node v14 to locate if script is a module or executable, like:
if(!module.parent){// running like `node myfile.js`}else{// running like require('./myfile.js')}
We had 206 files that usemodule.parent
And we want changes all occurrences ofmodule.parent
torequire.main
, that allows we check the same thing ofmodule.parent
.
if(require.main===module){/// running like `node myfile.js`}else{// running like require('./myfile.js')}
To change all occurrences ofmodule.parent
we used a codemod, with jscodeshift. Codemod is a tool/library to assist our with large-scale codebase refactors that can be partially automated.
But Eduardo, why you not use find and replace of your IDE?
R: Because this require a lot of attention and time of our developers, and if we not used codemod can't sure that can exists more module.parent on the future.
Time of code
We want change
if(!module.parent){}
to
if(require.main===module){}
How ?
We usedjscodeshift to codemod.
First you should add jscodeshift in your node_modules with
npm install -g jscodeshift
After this, you should create an archive that contains our codemod, in this case,replace-module-parent.js
.
First, we should create a function that is used in all files of the folder that we select, and pass two arguments,fileInfo
andapi
.
ThefileInfo
argument represents information of the currently processed file, andapi
is object that exposes thejscodeshift
library and helper functions from the runner.
// replace-module-parent.jsfunctiontransform(fileInfo,api){};module.exports=transform;
Now we want to get jscodeshift helpers, fromapi.jscodeshift
and transform our code to AST (Abstract System Types).
And you can explore more of our AST hereAST Explorer.
constj=api.jscodeshift;constroot=j(fileInfo.source)
Now, we want find all occurrences ofif(!module.parent)
, and replace toif(require.main === module)
// finding all ocurrences of if(!module.parent)root.find(j.IfStatement,{type:'IfStatement',test:{type:'UnaryExpression',operator:'!',argument:{type:'MemberExpression',object:{type:'Identifier',name:'module'},property:{type:'Identifier',name:'parent'}}}}).filter((path)=>{if(path.node.test.type!=='UnaryExpression'){returnfalse;}returntrue;})
Replacing all torequire.main
.forEach((path)=>{constrequireMain=j.ifStatement(j.binaryExpression('===',j.memberExpression(j.identifier('require'),j.identifier('main')),j.identifier('module')),path.node.consequent,path.node.alternate)j(path).replaceWith(requireMain)});returnroot.toSource();
And on final our codemod is
functiontransform(fileInfo,api){constj=api.jscodeshift;constroot=j(fileInfo.source)root.find(j.IfStatement,{type:'IfStatement',test:{type:'UnaryExpression',operator:'!',argument:{type:'MemberExpression',object:{type:'Identifier',name:'module'},property:{type:'Identifier',name:'parent'}}}}).filter((path)=>{if(path.node.test.type!=='UnaryExpression'){returnfalse;}returntrue;}).forEach((path)=>{constrequireMain=j.ifStatement(j.binaryExpression('===',j.memberExpression(j.identifier('require'),j.identifier('main')),j.identifier('module')),path.node.consequent,path.node.alternate)j(path).replaceWith(requireMain)});returnroot.toSource();};module.exports=transform;module.exports.parser='ts';
To run this code you can use this on your terminal:
jscodeshift -t replace-module-parent.js [your-input-files] -d -p
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse