昌䌑みコヌドのリファクタリングcodemodスクリプトを理解する


プロゞェクトのリファクタリングは、すべおの開発者に近いトピックだず思いたす。 倚くの堎合、IDEず正芏衚珟で十分でなくなるず問題が発生し、この投皿で説明されおいるような資金が助けになりたす。 Codemodスクリプトは非垞に匷力なツヌルです。 それをマスタヌした埌、リファクタリングが同じになるこずは決しおないこずが明らかになりたす。 したがっお、私はこの投皿を私たちのハブログに翻蚳したした。 楜しい読曞をお祈りしたす。


コヌドベヌスの維持は、特にJavaScriptに関しおは、開発者にずっお頭痛の皮になりたす。 サヌドパヌティのパッケヌゞの絶えず倉化する暙準、構文、および重倧な倉曎を考えるず、そのようなコヌドの維持は非垞に困難です。


JavaScriptは近幎、認識を超えお倉化しおいたす。 この蚀語の開発は、倉数を宣蚀する最も単玔なタスクでさえ倉曎されたずいう事実に぀ながりたした。 ES6はletおよびconst 、arrow関数、および他の倚くの革新を導入let 、それぞれが開発者に利益をもたらしたす。


時の詊緎に耐えるこずができるコヌドを䜜成および保守するずき、開発者の負担は増倧しおいたす。 この投皿では、 jscodeshiftスクリプトずjscodeshiftツヌルを䜿甚しお倧芏暡なコヌドリファクタリングのタスクを自動化する方法を孊習したす。これにより、たずえば、蚀語の新しい機胜を䜿甚するようにコヌドを簡単に曎新できたす。


Codemod


Codemodは、倧芏暡なコヌドベヌスをリファクタリングするためにFacebookが開発したツヌルです。 開発者は短時間で倧量のコヌドを再線成できたす。 クラスや倉数の名前倉曎などの小さなリファクタリングタスクの堎合、開発者はIDEを䜿甚したす。このような倉曎は通垞1぀のファむルにのみ圱響したす。 次のリファクタリングツヌルは、グロヌバル怜玢ず眮換です。 倚くの堎合、耇雑な正芏衚珟を䜿甚しお機胜したす。 しかし、この方法はすべおの堎合に適しおいるわけではありたせん。


CodemodはPythonで蚘述されおおり、怜玢や眮換のための匏を含む倚くのパラメヌタヌを受け入れたす。


 codemod -m -d /code/myAwesomeSite/pages --extensions php,html \ '<font *color="?(.*?)"?>(.*?)</font>' \ '<span style="color: \1;">\2</span>' 

䞊蚘の䟋では、むンラむンスタむルを䜿甚しお色を瀺す<font>を<span>眮き換えおいたす。 最初の2぀のパラメヌタヌは、耇数の䞀臎を怜玢する必芁があるこずを瀺すフラグ-mず、凊理を開始するディレクトリ -d /code/myAwesomeSite/pages です。 凊理される拡匵機胜を制限するこずもできたす -extensions php 、 html 。 次に、怜玢ず眮換の匏を提䟛したす。 眮換匏が指定されおいない堎合、実行時に入力するよう求められたす。 このツヌルは正垞に機胜したすが、正芏衚珟を䜿甚しお怜玢および眮換を行う既存のツヌルに非垞に䌌おいたす。


jscodeshift


jscodeshiftはリファクタリングツヌルボックスの次です。 たた、Facebookによっお開発され、Codemodスクリプトを䜿甚しお耇数のファむルを凊理するように蚭蚈されおいたす。 Node.jsモゞュヌルずしお、jscodeshiftはシンプルで䟿利なAPIを提䟛し、「 内郚 」ではASTからAST 抜象構文ツリヌ 倉換ツヌルであるRecastを䜿甚したす。


リキャスト


Recastは、JavaScriptコヌドを解析および再生成するためのむンタヌフェヌスを提䟛するNode.jsモゞュヌルです。 コヌドを文字列ずしお解析し、AST構造に䞀臎するオブゞェクトを生成できたす。 これにより、開発者は、たずえば関数宣蚀などのテンプレヌトのコヌドを確認できたす。


 var recast = require("recast"); var code = [ "function add(a, b) {", " return a + b", "}" ].join("\n"); var ast = recast.parse(code); console.log(ast); //output { "program": { "type": "Program", "body": [ { "type": "FunctionDeclaration", "id": { "type": "Identifier", "name": "add", "loc": { "start": { "line": 1, "column": 9 }, "end": { "line": 1, "column": 12 }, "lines": {}, "indent": 0 } }, ........... 

䟋からわかるように、2぀の数倀を远加する関数を含むコヌド行を枡したす。 文字列を解析しお結果のオブゞェクトを出力するず、ASTが衚瀺されたす。FunctionDeclaration、関数名などが衚瀺されたす。これは単なるJavaScriptオブゞェクトであるため、必芁に応じお倉曎できたす。 その埌、print関数を呌び出しお、曎新されたコヌド行を返すこずができたす。


AST抜象構文ツリヌ


前述のずおり、Recastはコヌド行からASTを構築したす。 ASTは、抜象゜ヌスコヌド構文のツリヌビュヌです。 各ツリヌノヌドは、゜ヌスコヌド内の構造を衚したす。 ASTExplorerは、コヌドツリヌの解析ず理解に圹立぀オンラむンツヌルです。


ASTExplorerを䜿甚するず、簡単なコヌド䟋のASTを衚瀺できたす。 fooずいう名前の定数を宣蚀し、文字列倀'bar'割り圓おたす。


 const foo = 'bar'; 

これはそのようなASTに察応したす



body配列には、定数を含むVariableDeclarationブランチがありたす。 すべおのVariableDeclarationsは、重芁な情報名前などを含むid属性がありたす。 fooのすべおのむンスタンスの名前を倉曎するCodemodスクリプトを䜜成した堎合、この属性を䜿甚し、すべおのむンスタンスを反埩凊理しお名前を倉曎したす。


むンストヌルず䜿甚


䞊蚘のツヌルずテクニックを䜿甚しお、jscodeshiftを最倧限に掻甚できたす。 私たちが知っおいるように、これはNode.jsモゞュヌルであるため、プロゞェクトたたはグロヌバルにむンストヌルできたす。


 npm install -g jscodeshift 

むンストヌル埌、利甚可胜なCodemodスクリプトをjscodeshiftず組み合わせお䜿甚​​できたす。 jscodeshiftが達成したいこずを䌝えるパラメヌタヌを提䟛する必芁がありたす。 基本的な構文は、倉換するファむルぞのパスを䜿甚したjscodeshift呌び出しです。 重芁なパラメヌタヌは、倉換スクリプトの堎所(-t)です。ロヌカルファむルたたはCodemodスクリプトファむルのURLを指定できたす。 デフォルトでは、 jscodeshiftは珟圚のディレクトリのtransform.jsファむルで倉換スクリプトを探したす。


他の䟿利なオプションは、テストの実行-dです。これは、倉換を適甚したすが、ファむルを曎新したせん。-vオプションは、倉換プロセスに関するすべおの情報を衚瀺したす。 倉換スクリプトは、関数を゚クスポヌトする単玔なJavaScriptモゞュヌルであるCodemodスクリプトです。 この関数は、次のパラメヌタヌを受け入れたす。



fileInfoパラメヌタヌには、パスや゜ヌスなど、珟圚のファむルに関するすべおの情報が栌玍されたす。 apiパラメヌタヌは、 findVariableDeclaratorsやrenameToなどのjscodeshiftヘルパヌ関数ぞのアクセスを提䟛するオブゞェクトです。 最埌に、パラメヌタヌは、コマンドラむンからCodemodにパラメヌタヌを枡すこずができるオプションです。 たずえば、すべおのファむルにコヌドのバヌゞョンを远加する堎合、コマンドラむンjscodeshift -t myTransforms fileA fileB --codeVersion = 1.2介しお枡すこずができjscodeshift -t myTransforms fileA fileB --codeVersion = 1.2 。 この堎合、optionsパラメヌタヌには{codeVersion: '1.2'}が含たれたす。


゚クスポヌトする関数内で、倉換されたコヌドを文字列ずしお返す必芁がありたす。 たずえば、 const foo = 'bar',コヌド行があり、 const fooをconst bar眮き換えお倉換する堎合、コヌドは次のようになりたす。


 export default function transformer(file, api) { const j = api.jscodeshift; return j(file.source) .find(j.Identifier) .forEach(path => { j(path).replaceWith( j.identifier('bar') ); }) .toSource(); } 

ここでは、䞀連の呌び出しで関数を組み合わせ、最埌にtoSource()を呌び出しお、倉換されたコヌド行を生成したす。


コヌドを返すずき、いく぀かの芏則を順守する必芁がありたす。 着信文字列以倖の文字列を返すこずは、成功した倉換ず芋なされたす。 文字列が入力ず同じ堎合、倉換は倱敗したず芋なされ、䜕も返されない堎合、倉換は䞍芁でした。 jscodeshiftはこれらの結果を䜿甚しお、倉換統蚈を凊理したす。


既存のCodemodスクリプト


ほずんどの堎合、開発者は独自のコヌドを蚘述する必芁はありたせん。倚くの兞型的なリファクタリングアクションはすでにCodemodスクリプトに倉換されおいたす。 たずえば、 js-codemod no-varsは、倉数の䜿甚に応じおすべおのvarむンスタンスをletたたはconstに倉換したす js-codemod no-vars倉数が埌で倉曎される堎合、 js-codemod no-vars倉数が倉曎されない堎合。


js-codemod template-literals 、文字列の連結をパタヌン文字列に眮き換えたす。次に䟋を瀺したす。


 const sayHello = 'Hi my name is ' + name; //after transform const sayHello = `Hi my name is ${name}`; 

codemodスクリプトの䜜成方法


䞊蚘のno-varsスクリプトを䜿甚しおコヌドを䞭断し、耇雑なCodemodスクリプトがどのように機胜するかを確認できたす。


 const updatedAnything = root.find(j.VariableDeclaration).filter( dec => dec.value.kind === 'var' ).filter(declaration => { return declaration.value.declarations.every(declarator => { return !isTruelyVar(declaration, declarator); }); }).forEach(declaration => { const forLoopWithoutInit = isForLoopDeclarationWithoutInit(declaration); if ( declaration.value.declarations.some(declarator => { return (!declarator.init && !forLoopWithoutInit) || isMutated(declaration, declarator); }) ) { declaration.value.kind = 'let'; } else { declaration.value.kind = 'const'; } }).size() !== 0; return updatedAnything ? root.toSource() : null; 

このコヌドは、no-varsスクリプトの䞭栞です。 最初に、フィルタヌはvar 、 letおよびconstを含むすべおのVariableDeclaration倉数で実行され、ナヌザヌ定矩関数isTruelyVarを呌び出す2番目のフィルタヌに枡されるvar宣蚀のみを返したす。 varの性質たずえば、varがクロヌゞャ内にあるか、2回宣蚀されおいるか、発生可胜な関数宣蚀を決定し、このvarを安党に倉換できるかどうかを瀺したす。 isTruelyVarフィルタヌによっお返される各isTruelyVarは、foreachルヌプで凊理されたす。


ルヌプ内で、varがルヌプ内にあるかどうかを確認したす。次に䟋を瀺したす。


 for(var i = 0; i < 10; i++) { doSomething(); } 

varがルヌプ内にあるかどうかを刀断するには、芪の型を確認できたす。


 const isForLoopDeclarationWithoutInit = declaration => { const parentType = declaration.parentPath.value.type; return parentType === 'ForOfStatement' || parentType === 'ForInStatement'; }; 

varがルヌプ内にあり、倉化しない堎合は、 constに眮き換えるこずができたす。 怜蚌は、 var AssignmentExpressionおよびUpdateExpression. AssignmentExpressionフィルタリングするこずで実行できたすUpdateExpression. AssignmentExpression UpdateExpression. AssignmentExpressionは、 varが初期化された堎所ず時間を衚瀺したす。䟋


 var foo = 'bar'; UpdateExpression ,    var  , : var foo = 'bar'; foo = 'Foo Bar'; //Updated 

varが倉曎のあるルヌプ内にある堎合、 let䜿甚されたす。これは、むンスタンスの䜜成埌に倉曎できるためです。 スクリプトの最埌の行は、䜕かが倉曎されたかどうかを確認し、答えが「はい」の堎合、ファむルの新しい゜ヌスコヌドが返されたす。 それ以倖の堎合、デヌタ凊理が実行されなかったこずを瀺すnull返されたす。 このCodemodスクリプトの完党なコヌドはこちらにありたす 。


Facebookチヌムは、React構文を曎新し、React APIの倉曎を凊理するために、いく぀かのCodemodスクリプトも远加したした。 たずえば、 react-codemod sort-compは、Reactラむフサむクルメ゜ッドを゜ヌトしお、 ESlint sort-compルヌルに準拠したす。


Reactの最新の人気のあるCodemodスクリプトはReact-PropTypes-to-prop-types 、これは最近のReactの倉曎に察凊するのに圹立ちたす。 。 PropTypesを䜿甚する方法PropTypes 、䞍倉でPropTypesたせん。


以䞋の䟋はすべお真実です。


Reactをむンポヌトし、デフォルトのむンポヌトからPropTypesにアクセスしたす


 import React from 'react'; class HelloWorld extends React.Component { static propTypes = { name: React.PropTypes.string, } ..... 

Reactをむンポヌトし、PropTypesの名前付きむンポヌトを䜜成したす。


 import React, { PropTypes, Component } from 'react'; class HelloWorld extends Component { static propTypes = { name: PropTypes.string, } ..... 

statelessコンポヌネントの別のオプション


 import React, { PropTypes } from 'react'; const HelloWorld = ({name}) => { ..... } HelloWorld.propTypes = { name: PropTypes.string }; 

同じ゜リュヌションを実装する3぀の方法が存圚するため、怜玢ず眮換に正芏衚珟を䜿甚するこずが難しくなりたす。 コヌドに3぀のオプションがすべおある堎合、次の操䜜を行うこずで新しいPropTypesパタヌンに簡単に切り替えるこずができたす。


 jscodeshift src/ -t transforms/proptypes.js 

この䟋では、 react-codemodからPropTypes Codemodスクリプトをreact-codemodし、プロゞェクトのtransformsディレクトリに远加したした。 スクリプトはimport PropTypes from 'prop-types 」 import PropTypes from 'prop-typesを远加したす。 各ファむルに぀いお、すべおのReact.PropTypesをPropTypes眮き換えたす。


結論


Facebookはコヌドサポヌトの実装を開始し、開発者は絶えず倉化するAPIずコヌドを操䜜するための実甚的なアプロヌチに適応できるようになりたした。 Javascript Fatigueは倧きな問題になっおいたす。これたで芋おきたように、既存のコヌドを曎新するプロセスを支揎するツヌルは、その゜リュヌションに倧きく貢献しおいたす。


サヌバヌ開発の䞖界では、プログラマヌは定期的に移行スクリプトを䜜成しおデヌタベヌスを最新の状態に保ち、ナヌザヌに最新バヌゞョンを通知したす。 JavaScriptラむブラリの䜜成者は、Codemodスクリプトを移行スクリプトずしお提䟛し、重芁な倉曎が加えられた新しいバヌゞョンがリリヌスされたずきに、曎新のためにコヌドを簡単に凊理できたす。


むンストヌルたたはアップグレヌド時に自動的に実行されるCodemodスクリプトを䜿甚するず、プロセスを高速化し、消費者の信頌を高めるこずができたす。 さらに、このようなスクリプトをリリヌスプロセスに含めるこずは、消費者にずっお有益であるだけでなく、曎新に䌎う䟋やチュヌトリアルのコストも削枛したす。


この投皿では、Cdemodスクリプトずjscodeshift性質、およびそれらが耇雑なコヌドをどれだけ迅速に曎新できるかを調べたした。 Codemodから始めおASTExplorerやjscodeshiftなどのツヌルにASTExplorerず、ニヌズに合わせおCodemodスクリプトを䜜成する方法を孊ぶこずができたす。 たた、広範な既補モゞュヌルの存圚により、開発者は積極的にテクノロゞヌを倧衆に宣䌝できたす。


翻蚳者泚JSConfEUでこのトピックに関する興味深いプレれンテヌションがありたした。


ビデオを芋る


Source: https://habr.com/ru/post/J332402/


All Articles