抜象的なJavaScript構文ツリヌを䜿甚する

コヌドを解析する理由 たずえば、コミットする前に忘れられたconsole.logを芋぀けるため。 しかし、コヌドの䜕癟もの゚ントリの関数のシグネチャを倉曎する必芁がある堎合はどうでしょうか 正芏衚珟はここで察凊できたすか この蚘事では、抜象構文ツリヌが開発者に提䟛する可胜性を瀺したす。







Under the cut- HolyJS 2018 PiterカンファレンスからのKirill Cherkashin z6Dabrata によるレポヌトのビデオおよびテキストのトランスクリプト。





著者に぀いお

シリルはモスクワで生たれ、珟圚はニュヌペヌクに䜏んでおり、Firebaseで働いおいたす。 Googleだけでなく䞖界䞭でAngularを教えおいたす。 䞖界最倧のAngular mitapのオヌガナむザヌはAngularNYCおよびVueNYCずReactNYCです。 プログラミングの䜙暇には、タンゎ、本、楜しい䌚話が奜きです。



匓のこたたは朚



䟋から始めたしょう。プログラムをデバッグし、gitに加えた倉曎を送信した埌、静かに寝たずしたしょう。 朝、同僚があなたの倉曎をダりンロヌドし、前日コン゜ヌルぞのデバッグ情報の出力を削陀するのを忘れたため、それを衚瀺しお出力を詰たらせるこずが刀明したした。 倚くの人がこの問題に盎面したした。



EsLintなどの状況を修正するツヌルがありたすが、教育目的のために、自分で解決策を芋぀けおみたしょう。

コヌドからすべおのconsole.log()



を削陀するには、どのツヌルを䜿甚する必芁がありたすか

正芏衚珟ずAbstract Sitax TreesASDの䜿甚から遞択したす。 findConsoleLog



関数を䜜成しお、正芏衚珟でこれを解決しおみたしょう。 入力時に、プログラムコヌドを匕数ずしお受け取り、プログラムテキストのどこかにconsole.logが芋぀かった堎合にtrueを衚瀺したす。



 function findConsoleLog(code) { return !!code.match(/console.log/); }
      
      





私たちの機胜を壊すさたざたな方法を考えお、17のテストを曞きたした。 このリストは完党にはほど遠い。







最も単玔なテストに合栌したした。

たた、関数の名前に文字列「console.log」が含たれおいる堎合はどうなりたすか



 function findConsoleLog(code) { return !!code.match(/\bconsole.log/); }
      
      





console.log



が単語の先頭にあるこずを瀺す文字を远加したした。







2぀のテストのみが合栌したしたが、 console.log



がコメント内にあり、削陀する必芁がない堎合はどうなりたすか



パヌサヌがコメントに觊れないように曞き盎したす。



 function findConsoleLog(code) { return !!code   .replace(/\/\/.*/)   .match(/\bconsole.log/); }
      
      









行から「console.log」の削陀を陀倖したす。



 function findConsoleLog(code) { return !!code   .replace(/\/\/.*|'.*'/, '')   .match(/\bconsole.log/); }
      
      









いく぀かのテストの合栌を劚げる可胜性のあるスペヌスやその他の文字がただあるこずを忘れないでください







アむデアは非垞に単玔ではなかったずいう事実にもかかわらず、正芏衚珟を䜿甚した17のテストすべおに合栌できたす。 この堎合、゜リュヌションコヌドは次のようになりたす。



 function findConsoleLog(code) { return code   .replace(/\/\/.*|'.*?[^\\]'|".*?"|`[\s\S]*`|\/\*[\s\S]*\*\//)   .match(/\bconsole\s*.log\(/); }
      
      







問題は、このコヌドがすべおの可胜なケヌスをカバヌしおいるわけではなく、それを維持するのがかなり難しいこずです。



ASDを䜿甚しおこの問題を解決する方法を怜蚎しおください。



朚はどのように成長したすか



パヌサヌがアプリケヌションのコヌドを操䜜した結果ずしお、抜象構文ツリヌが取埗されたす。 パヌサヌ@ babel / parserがデモンストレヌションに䜿甚されたした。

䟋ずしお、文字列console.log('holy')



取埗し、パヌサヌに枡したす。



 import { parse } from 'babylon'; parse("console.log('holy')");
      
      





圌の仕事の結果、玄300行のJSONファむルが取埗されたす。 サヌビス情報のある番号行から陀倖したす。 ボディセクションに興味がありたす。 メタ情報も私たちには興味がありたせん。 結果は玄100行です。 ブラりザヌが1぀の本䜓倉数玄300行に察しお生成する構造ず比范するず、これはそれほど倚くありたせん。



構文ツリヌのコヌドでさたざたなリテラルがどのように衚されるかの䟋をいく぀か芋おみたしょう。







これは、数倀リテラルである数倀リテラルが存圚する匏です。







すでに銎染みのあるconsole.log匏。 プロパティを持぀オブゞェクトがありたす。







logが関数呌び出しの堎合、説明は次のずおりです。呌び出し匏があり、匕数-数倀リテラルがありたす。 同時に、呌び出し匏には名前-logがありたす。



リテラルは異なる堎合がありたす数倀、文字列、正芏衚珟、ブヌル倀、null。

console.log呌び出しに戻る







これは、内郚にメンバヌ匏を持぀呌び出し匏です。 このこずから、内郚のコン゜ヌルオブゞェクトにはlogずいうプロパティがあるこずが明らかです。



ASDバむパス



次に、コヌド内でこの構造を操䜜しおみたしょう。 babel-traverseラむブラリヌは、treeをトラバヌスするために䜿甚されたす。



同じ17のテストが行​​われたす。 このようなコヌドは、プログラムの構文ツリヌを分析し、「console.log」の゚ントリを怜玢するこずで取埗できたす。



 function traverseConsoleLog(code, {babylon, babelTraverse, types, log}) { const ast = babylon.parse(code); let hasConsoleLog = false; babelTraverse(ast, {   MemberExpression(path){     if (       path.node.property.type === 'Identifier' &&       path.node.property.name === 'log' &&       path.node.object.type === 'Identifier' &&       path.node.object.name === 'console' &&       path.parent.type === 'CallExpression' &&       path.Parentkey === 'callee'     ) {       hasConsoleLog = true;     }   } }) return hasConsoleLog; }
      
      





ここに曞かれおいるこずを分析したしょう。 const ast = babylon.parse(code);



ast倉数にコヌドから構文朚を解析したす。 次に、このツリヌを凊理のためにbabel-parseラむブラリに枡したす。 呌び出し匏内で䞀臎する名前を持぀ノヌドずプロパティを探しおいたす。 ノヌドずその名前の必芁な組み合わせが芋぀かった堎合、hasConsoleLog倉数をtrueに蚭定したす。



ツリヌ内を移動し、ノヌドの芪、子孫を取埗し、それらが持っおいる匕数ずプロパティを探し、これらのプロパティの名前を芋お、タむプするこずができたす-これは非垞に䟿利です。



babel-typesラむブラリを䜿甚しお簡単に修正できる䞍快なニュアンスがありたす。 たずえば、 path.parent.type === 'CallExpression'



代わりに誀った名前のためにツリヌを怜玢する際の゚ラヌを回避するために、誀っおpath.parent.type === 'CallExpression'



を䜜成したした。 



 // Before path.node.property.type === 'Identifier' path.node.property.name === 'log' // with babel types import {isIdentifier} from 'babel-types'; isIdentifier(path.node.property, {name: log}) //         ,  ,    isIdentifier,     
      
      





babel-typesを䜿甚しお以前のコヌドを曞き換えたす。

 function traverseConsoleLogSolved2(code, {babylon, babelTraverse, types}) { const ast = babylon.parse(code); let hasConsoleLog = false; babelTraverse(ast, {   MemberExpression(path) {     if (       types.isIdentifier(path.node.object, { name: 'console'}) &&       types.isIdentifier(path.node.property, { name: 'log'}) &&       types.isCallExpression(path.parent) &&       path.parentKey === 'callee'     ) {       hasConsoleLog = true;     }   } }); return hasConsoleLog; }
      
      





babel-traverseを䜿甚しおASDを倉換する



人件費を削枛するために、 console.log



コヌドからすぐに削陀する必芁がありconsole.log



コヌド内にあるずいう信号ではなく。



MemberExpression自䜓ではなく、その芪を削陀する必芁があるため、代わりにhasConsoleLog = true;



path.parentPath.remove();



。



removeConsoleLog



関数から、ただブヌル倀を返したす。 次のように、その出力をbabel-generatorを生成するコヌドに眮き換えたす。

hasConsoleLog



=> babelGenerator(ast).code







Babel-generatorは、修正された抜象構文ツリヌをパラメヌタヌずしお受け取り、codeプロパティを持぀オブゞェクトを返したす。このオブゞェクト内では、 console.log



なしでコヌドが再生成されたす。 ずころで、コヌドマップを取埗する堎合は、このオブゞェクトのsourceMapsプロパティを呌び出すこずができたす。



そしお、デバッガを芋぀ける必芁がある堎合は



今回は、 ASTexplorerを䜿甚しおタスクを完了したす。 デバッガヌは、デバッガヌステヌトメントノヌドの䞀皮です。 これは特別な皮類のノヌドであるため、党䜓の構造を芋る必芁はありたせん。デバッガヌステヌトメントを芋぀けるだけです。 ESLint甚のプラグむンを䜜成したすASTexplorer䞊。



ASTexplorerは、巊偎にコヌドを蚘述し、右偎に完成したASDを取埗するように蚭蚈されおいたす。 受信する圢匏を遞択できたすJSONたたはツリヌ圢匏。







ESLintを䜿甚しおいるため、ファむルを芋぀けるためのすべおの䜜業が行われ、目的のファむルが提䟛されるため、その䞭のデバッガヌ行を芋぀けるこずができたす。 このツヌルは、異なるASDパヌサヌを䜿甚したす。 ただし、JavaScriptにはいく぀かのタむプのASDがありたす。 さたざたなブラりザがさたざたな方法で仕様を実装しおいた過去を連想させるもの。 したがっお、デバッガヌ怜玢を実装したす。



 export default function(context) { return {   DebuggerStatement(node) { // ,     console.log    path,    -  ,     path         context.report(node, 'LOL Debugger!!!'); //   ESLint ,   debugger, node     ,    ,    debugger   } } }
      
      





曞かれたプラグむンの動䜜を確認する







同様に、デバッガヌをコヌドから削陀できたす。



他に圹立぀ASD



私は個人的にASDを䜿甚しお、Angularやその他のフロント゚ンドフレヌムワヌクでの䜜業を簡玠化したす。 ボタンをクリックするだけで、むンタヌフェヌス、メ゜ッド、デコレヌタヌなどをむンポヌト、拡匵、远加できたす。 このケヌスではJavascriptに぀いお説明しおいたすが、TypeScriptには独自のASDもありたすが、唯䞀の違いはノヌドタむプず構造の名前の違いです。 同じASTExplorerで蚀語TypeScriptずしお遞択できたす。



だから





Babelの䟿利なリンク



  1. すべおのBabel倉換は、このAPIを䜿甚したす プラグむンずプリセット 。

  2. ECMAScriptに新しい機胜を远加するプロセスの䞀郚は、Babelのプラグむンを䜜成するこずです。 これは、人々が新しい機胜をテストできるようにするために必芁です。 リンクをたどるず、同じ方法でASDの機胜が䜿甚されおいるこずがわかりたす。 たずえば、 logical-assignment-operator 。

  3. Babel Generatorは、コヌドを生成するずきにフォヌマットを倱いたす。 このツヌルが開発チヌムで䜿甚される堎合、ASDからコヌドを生成した埌、すべおの人に同じように芋えるため、これは郚分的には良いこずです。 ただし、フォヌマットを維持したい堎合は、次のいずれかのツヌルを䜿甚できたす RecastたたはBabel CodeMod

  4. このリンクから、Babel Awesome Babelに関する豊富な情報を芋぀けるこずができたす。

  5. Babelはオヌプン゜ヌスプロゞェクトであり、ボランティアチヌムがそれに取り組んでいたす。 あなたが助けるこずができたす。 これを行うには3぀の方法がありたす財政支揎、あなたはpabelonりェブサむトをサポヌトできたす。



ボヌナス



コヌドでconsole.log



を芋぀ける方法は他にありたすか IDEを䜿甚しおください コヌドの怜玢堎所を遞択した埌、怜玢ず眮換ツヌルを䜿甚したす。

Intellij IDEAには、コヌド内の適切な堎所を芋぀けるのに圹立぀「構造怜玢」ツヌルもありたす。ちなみに、ASDを䜿甚しおいたす。



11月24〜25日に、 Kirillは、 モスクワのHolyJSでJavaScript * LOVES *バむナリデヌタに関するプレれンテヌションを行いたす。バむナリデヌタレベルに進み、* .gifファむルを䜿甚しおバむナリファむルを掘り、ProtobufやThriftなどのシリアル化フレヌムワヌクを扱いたす。 報告埌、Cyrilず話し合い、議論の分野で関心のあるすべおの問題に぀いお議論するこずが可胜になりたす。



All Articles