Scripted 中的依賴分析

工程 | Kris De Volder | 2012年11月20日 | ...

VMWare 的 JavaScript 編輯器 Scripted 已於上月在此部落格上釋出。在本文中,我們將深入瞭解 Scripted 的 依賴分析引擎。但在深入探討細節之前,我們先來闡述一下為什麼我們需要它。

主要動機:跨檔案內容輔助

為了提供卓越的 JavaScript 編輯體驗,Scripted 需要提供關於您在當前編輯器上下文中可以使用的函式、方法或識別符號的準確建議。

[caption id="attachment_12178" align="aligncenter" width="533" caption="跨檔案內容輔助"][/caption]

兩個元件協同工作以實現此目標

  • 一個細粒度的型別推斷分析引擎
  • 一個粗粒度的依賴分析引擎
推斷引擎解析您的程式碼並遍歷每個宣告、語句和表示式。這使得它能夠確定在給定上下文中哪些識別符號是有效的,並很好地猜測可能儲存在這些變數中的事物種類。然後,此資訊用於提供內容輔助建議。

如果您只是想將所有程式碼放入一個大檔案中,那麼一個高質量的推斷器就足以提供相當不錯的內容輔助。實際上,專案將被劃分為模組。在上面的示例中,'main' 模組匯入了一個 'utils' 模組。當您編輯 main 模組時,Scripted 會適當地從 'utils' 建議函式。依賴分析引擎使這成為可能。

依賴分析的其他用途

Scripted 還使用依賴分析將未解決的依賴項標記為錯誤。例如,如果我們嘗試匯入一個 'bogus' 模組,Scripted 將顯示一個錯誤標記

[caption id="attachment_12180" align="aligncenter" width="518" caption="未解決模組的錯誤標記"][/caption]

Scripted 還使用依賴分析來支援輕鬆導航。在已解決的依賴項名稱上按 Shift 鍵或 Ctrl 鍵並單擊將帶您到相應的檔案。

將來,依賴分析還可能允許我們實現重構工具。例如,如果您將 .js 檔案拖放到不同的目錄,Scripted 可以根據需要自動更新相對路徑引用。

它做什麼?

依賴分析引擎僅向其客戶端(型別推斷引擎)提供一個 getDGraph 函式
getDGraph :: <path-to-js-file> -> <dependency-graph>
此函式計算依賴圖的 JSON 表示。此圖包含目標檔案直接或間接依賴的所有檔案的節點。如果我們將 'main' 模組傳遞給它,它將返回如下內容
getDGraph('/sample/main.js') ==>
{
  ...
  "/NODE-NATIVES/stream.js": {
    "kind": "commonjs",
    "refs": { ... }
  },
  "/NODE-NATIVES/fs.js": {
    "kind": "commonjs",
    "refs": {
      "stream": {
        "kind": "commonjs",
        "name": "stream",
        "path": "/NODE-NATIVES/stream.js"
      },
      ...
    }
  },
  "/sample/utils.js": {
    "kind": "commonjs",
    "refs": {}
  },
  "/sample/main.js": {
    "kind": "commonjs",
    "refs": {
      "fs": {
        "kind": "commonjs",
        "name": "fs",
        "path": "/NODE-NATIVES/fs.js"
      },
      "./utils": {
        "kind": "commonjs",
        "name": "./utils",
        "path": "/sample/utils.js"
      }
    }
  }
}
此 JSON 物件中的每個屬性都代表圖中的一個節點。'refs' 屬性包含邊。每條邊對應一個模組匯入。

一個有趣的細節是,依賴分析器為原生 Node 模組返回特殊的 path 字串。當推斷引擎請求此類路徑的原始碼時,Scripted 伺服器(用 JavaScript 編寫並執行在 Node.js 上)從其自己的 Node.js 程序中提取原始碼。推斷引擎像分析普通 JavaScript 檔案一樣分析它。結果是為內建 Node 模組提供了不錯的內容輔助

[caption id="attachment_12184" align="aligncenter" width="587" caption="內建 Node 模組的推斷建議"][/caption]

支援哪些模組系統?

目前我們僅支援 AMD 和 CommonJS。對於 CommonJS,我們使用 enhanced-resolve 庫來解析對路徑的引用。對於 AMD,我們目前使用自定義解析器。如果我們找到一個符合我們需求的現有庫,我們可能會用它替換。

它是如何工作的?

該過程從 目標檔案(即傳遞給 getDGraph 函式的引數)開始。它大致按以下步驟進行
  1. 檢測模組型別(CommonJS 與 AMD)。
  2. 在目標模組中查詢引用。
  3. 解析引用(即確定將為引用載入的實際檔案的路徑)。
  4. 對每個已解析的引用重複步驟 1 中的過程。
步驟 1 基於檢測一些典型的程式碼模式。例如,“不在 'define' 呼叫內巢狀的 'require' 呼叫”是一個跡象,表明我們正在處理 CommonJS 模組。

步驟 2 和 3 根據步驟 1 中的模組型別分派給不同的支援模組。新增對其他模組型別的支援應該相對容易(前提是步驟 1 檢測器可以識別新的模組型別)。

自動解析器配置

依賴分析器嘗試發現它需要知道什麼,而不是要求手動配置。這是 Scripted 的一般理念,依賴分析器也不例外。事實上,目前無法手動配置依賴分析器或其任何元件,儘管我們將來可能會使其成為可能。

對於 Node/CommonJS,這工作得很好,主要是因為實際上沒有什麼需要配置的。也就是說,如果我們假設使用標準的 Node.js 載入器演算法,那就是我們所需的所有資訊。

對於 AMD,情況不幸更復雜。典型的 AMD 載入器(例如 requirejs)是高度可配置的。此外,這種配置在專案原始碼中表達的方式往往因專案而異。這使得在一個隨機專案中確定在哪裡找到所需資訊成為一個挑戰。

我們採取的方法是檢視一些示例專案以及它們使用的“典型”模式。發現透過識別這些模式來工作。希望如果我們支援足夠多的常見模式,最終發現將適用於大多數專案。對於那些失敗的情況,我們可能還會新增一些手動配置選項作為最後手段。

為了說明 AMD 發現是如何工作的,這裡有一個我們目前檢測到的模式示例

這裡的模式是一個帶有 data-main 屬性的 script 標籤……如果 Scripted 發現這一點,它將會在 data-main 檔案中查詢 requirejs 風格的配置塊

AMD 配置發現正在進行中。隨著我們獲得更多關於人們如何設定 AMD 載入器的示例,我們嘗試為它們新增檢測器。如果 Scripted 錯誤地將許多依賴項標記為錯誤,則它可能未能發現您的 AMD 配置。您可以透過提出錯誤請求來幫助我們。如果您附上說明您的“典型模式”的程式碼示例。這將有助於我們擴充套件我們的“模式目錄”。

結論

我們深入瞭解了 Scripted 的內部。我們介紹了 Scripted 依賴分析引擎。它目前支援 AMD 和 CommonJS 模組系統。依賴分析為型別推斷引擎提供了依賴圖。基於此圖的跨檔案分析使我們能夠為在其他模組中定義的功能提供準確的內容輔助建議。依賴資訊還用於為無法解析的模組引用建立錯誤標記,並允許快速導航到已解析的依賴項。將來,依賴分析還可能實現準確的重構工具,以移動或重新命名模組。

想嘗試 Scripted 嗎?從 GitHub 獲取它。它易於安裝。下載和安裝說明在 readme 檔案中。

連結

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有