Scripted 的依賴分析

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

Scripted 是 VMWare 出品的 JavaScript 編輯器,已於上個月在本部落格宣布。在本文中,我們將深入探討 Scripted 的依賴性分析引擎。但在深入細節之前,讓我們先說明為何需要它。

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

為了提供優良的 JavaScript 編輯體驗,Scripted 需要針對您在目前編輯器環境中可以使用的函式、方法或識別字提供精確的建議。

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

兩個組件協同運作以達成此目標

  • 一個細粒度的型別推論分析引擎
  • 一個粗粒度的依賴性分析引擎
推論引擎會解析您的程式碼,並逐一檢視每個宣告、陳述式和運算式。這使其能夠判斷在給定環境中哪些識別字是有效的,並對可能儲存在這些變數中的事物種類做出合理的猜測。然後,這些資訊會用於產生內容輔助建議。

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

依賴性分析的其他用途

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

[圖說 id="attachment_12180" align="aligncenter" width="518" 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 模組傳回特殊的 路徑 字串。當推論引擎請求此路徑的原始碼時,以 JavaScript 編寫並在 Node.js 上執行的 Scripted 伺服器會從其自身的 Node.js 程序中提取原始碼。推論引擎會像分析普通的 JavaScript 檔案一樣分析它。結果是為內建 Node 模組提供良好的內容輔助。

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

支援哪些模組系統?

目前我們僅支援 AMD 和 CommonJS。對於 CommonJS,我們使用 enhanced-resolve 程式庫來解析對路徑的參考。對於 AMD,我們目前使用自製的解析器。如果我們找到符合需求的現有程式庫,我們可能會將其替換。

它是如何運作的?

此流程從目標檔案(即傳遞給 getDGraph 函式的引數)開始。大致依循以下步驟進行
  1. 偵測模組類型(CommonJS 或 AMD)。
  2. 在目標模組中尋找參考。
  3. 解析參考(即判斷將為參考載入的實際檔案路徑)。
  4. 針對每個已解析的參考,從步驟 1 重複此流程。
步驟 1 是基於偵測一些典型的程式碼模式。例如,「對 'require' 的呼叫未巢狀於 'define' 呼叫內」是我們正在處理 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 社群中所有即將到來的活動。

檢視全部