手把手教你寫幾個實用的的AST插件

Javascript AST

背景

AST 是很是有用的javascript

今天下午聽了小組一個老哥作的AST分享,深覺得然。前端

爲了加深印象,就寫了篇總結,順便分享給你們,但願能給朋友們一些啓發。java

AST有用,口說無憑, 且看幾個具體的案例node

且不說:react

  1. Vue => React
  2. React => Vue

的代碼轉換方法,git

咱們就看一個能夠無痛升級舊版React的工具:github

react-codemodnpm

代碼地址:https://github.com/reactjs/re...segmentfault

這個工具,功能十分強大,使用起來也很方便,只須要運行一行命令:api

npx react-codemod <transform> <path> [...options]

這些無不借助了AST。

下面咱們就進入今天的內容。

正文

本文的主要內容包括:

  • 1 理論: AST 概念
  • 2 實踐: 使用 AST 實現一個代碼轉換工具, 把 var轉換成let
  • 3 實踐: 使用 AST 實現一個Eslint 插件, 禁用 console
  • 4 實踐: 使用 AST 實現一個Babel插件, 過濾 Debugger

1. AST 基本概念

AST 是什麼

AST is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.

在計算機科學中,抽象語法抽象語法樹實際上是源代碼的抽象語法結構的樹狀表現形式

經常使用的瀏覽器就是經過將js代碼轉化爲抽象語法樹來進行下一步的分析等其餘操做。

因此將js轉化爲抽象語法樹更利於程序的分析。

AST 能作什麼

  • 代碼語法的檢查
  • 代碼風格的檢查
  • 代碼的格式化
  • 代碼的高亮
  • 代碼錯誤提示
  • 代碼自動補全
  • 等等。

AST 三板斧

  • 生成AST
  • 遍歷和更新AST
  • 將AST從新生成源碼

爲了便於理解, 咱們看一個具體的例子。

這裏順便給你們介紹一個十分有用的網站: https://astexplorer.net/

你譬如:

image.png

聲明, 變量, 類型等各類信息包羅萬象。

並且這裏也提供了各類插件模版供你選擇:

image.png

十分的方便。

咱們就根據這個例子, 咱們作個小的實踐。

2. 實踐: 使用AST實現一個代碼轉換工具, 把var轉換成let

好比, 如今要重構一個老項目, 你要把項目裏的var 所有替換成let, 你會怎麼作?

手動替換? 或者藉助工具一鍵替換?

如今就教你一招:一鍵替換大法

首先仍是要介紹一把大殺器: jscodeshift

jscodeshift 是一個 Javscript Codemod 工具,官方對 Codemod 的解釋是:

Codemod is a tool/library to assist you with large-scale codebase refactors that can be partially automated but still require human oversight and occasional intervention.

jscodeshift 也是基於 esprima 的,其經過 path 能夠很容易的在 AST 上遍歷 node

如今咱們就開始替換項目中的var.

首先,到 https://astexplorer.net 裏面編寫代碼.

模板咱們選: jscodeshift

image.png

官方自帶的例子, 把變量名字反轉:

image.png

咱們如今要改變量, 這個工具很貼心的一點是能夠高亮實時對照,

image.png

如今, 找到kind === var 的對象, 替換成let:

獲得以下代碼:

export default function transformer(file, api) {
  const j = api.jscodeshift;

  return j(file.source)
    .find(j.VariableDeclaration, { kind: 'var'})
    .forEach(path => {
      const letStatement = j.variableDeclaration('let', path.node.declarations)
      j(path).replaceWith(letStatement)
    })
    .toSource();
}

這樣也能夠:

path.node.kind = 'let'; // 傳入的實際是一個引用

實際效果:

image.png

大功告成!

假如我項目裏有幾個文件也須要相同的操做:

簡單安裝:

sudo npm install -g jscodeshift

執行:

jscodeshift -t transform.js ./src/demo.js --dry --print

image.png

這裏用了--dry 和 --print

--dry 加上以後,不會馬上把新生成的代碼覆蓋源文件

--print 是打印出來看看

在實際項目裏, 你須要在獨立的分支裏操做,新生成代碼以後, 須要你再檢查檢查review沒有問題以後才能合併。

3. 使用AST實現一個Eslint 插件, 禁用console

和上面的相似, 咱們也能夠作一個eslint 插件, 功能也很簡單: 檢查到使用console的時候就報錯

指望達到的效果:

// Do not use console methods (at 1:9)
   console.log('haha')
// --------^

咱們此次選擇 babel-eslint 模版。

代碼實現:

const disallowMethods = ["log", "info", "warn", "error", "dir"];
export default function(context) {
  return {
    Identifier(node) {
      const isConsoleMethod =
        disallowMethods.includes(node.name) &&
        node.parent.type === "MemberExpression" &&
        node.parent.object.name === "console";

      if (!isConsoleMethod) return;

      context.report({
        node,
        message: "Do not use console methods"
      });
    }
  };
}

實際效果:

image.png

簡單有效。

不過你要是非要玩什麼騷操做,好比自定義一個log, 那就沒得搞了。

最後, 你能夠把這段代碼封裝成一個完整的插件:

教你如何編寫 Eslint 插件

你能夠自行實踐。

4. 使用AST實現一個Babel插件, 過濾debugger

最後一個是過濾源代碼中的debugger, Transform 咱們選擇babelv7

這個插件,咱們指望達到的效果是:

var a = 1
debugger
function test() {
  debugger
   a++
}
debugger

到:

var a = 1;

function test() {
  a++;
}

這也是一個十分有用的功能。

代碼實現:

export default function (babel) {
    const {
        types: t
    } = babel;

    return {
        name: "ast-transform", // not required
        visitor: {
            DebuggerStatement(path) {
                path.remove()
            }
        }
    };
}

實際效果:

image.png

總結

內容大概就是這麼多,沒什麼難度,重在講述理論和入門

對AST還不熟練的同窗, 但願這篇能夠幫助到你。

後面還有會AST在咱們實際項目中的應用, 我也會寫一個實戰篇, 敬請期待!

以上。

延伸閱讀

https://www.toptal.com/javasc...

最後

若是以爲內容有幫助,能夠關注下個人公衆號 「 前端e進階 」,一塊兒學習。

clipboard.png

相關文章
相關標籤/搜索