ClojureScript

自訂 REPL

本頁記錄了對使用 cljs.repl 中提供的功能的自訂 REPL 的要求的最新變更。這些變更的目的是大幅縮短所有 ClojureScript REPL 的啟動時間,並簡化 REPL 狀態與已編譯原始碼的同步。這是透過重複使用全域可用的編譯快取基礎架構來實現的。事實上,目前可以啟動將 :output-dir 設定為現有編譯快取的 REPL,而無需進行任何分析或編譯。

在新的基礎架構下,所有內建 REPL 都可以在現代硬體上於一秒內或更短的時間內啟動。

期望

為了盡快啟動 REPL,REPL 必須實作新的 2 個引數的 -setup 函數,該函數會採用典型的編譯器建置選項。在過去,-setup 允許是非同步的 - 現在不再支援此功能,REPL 現在必須在 -setup 期間編譯和載入 cljs.core 及其所有依賴項。在 -setup 中,REPL 應使用建置選項將編譯後的 JavaScript 和分析資訊快取到預期的位置。請注意,雖然可以串流使用者輸入的已編譯表單,但應盡一切可能避免載入命名空間 - REPL 應依賴目標環境來解釋 goog.require。這有許多好處,包括精確的原始碼對應資訊。

新的 Node.js REPL 是新模式的一個很好的例子。Node.js REPL 很短,因為它依賴 Node.js 執行環境本身來解釋 goog.require

檢查 cljs.repl/load-filecljs.repl/load-namespace 將闡明新方法

  • 給定一個命名空間,確保它已編譯。

  • 計算檔案的 goog.addDependency 字串並評估它。

  • 為命名空間發出 goog.require 陳述式並評估它。

REPL 應該覆寫全域 CLOSURE_IMPORT_SCRIPT 函式以取得自訂的 goog.require 行為。

消除已載入程式庫的追蹤

在新變更下,REPL 不需要再麻煩地直接在其 Clojure 實作中明確追蹤已載入的程式庫。相反,REPL 應安排確保 JavaScript 評估環境符合 cljs.core/loaded-libs,必要時將所需的邏輯嵌入 CLOSURE_IMPORT_SCRIPT 中。

歷史:這以前只是因為如果已載入命名空間,goog.provide 會擲出例外。這是一個完全錯誤的錯誤,旨在教「初學者」。透過猴子修補 goog.isProvided_ 使其成為永遠傳回 false 的函式,可以抑制該錯誤。同樣,Node.js REPL 也是這種修補以及在 CLOSURE_IMPORT_SCRIPT 實作中符合 loaded-libs 的一個很好的例子。

特殊函式

所有 REPL 都支援幾個「特殊函式」。特殊函式必須採用 REPL 環境、分析環境、表單和(選擇性)編譯器建置選項。開箱即用的提供了 in-nsrequireload-fileload-namespace

輸出

自訂 REPL 不應直接呼叫 printlnprintflush,而應符合傳遞給 -setupopts(第二個引數)中與 :print:print-no-newline:flush 相關聯的值。另請注意,與 :print:print-no-newline 相關聯的函式只接受一個引數。

原始碼對應

現在,所有 REPL 都可以實作一個新的協定,以便「免費」取得原始碼對應支援。如果評估的結果為 :exception,則如果 REPL 評估環境符合 cljs.repl/IParseStacktrace,則 REPL 基礎架構會叫用 -parse-stacktrace。REPL 評估環境將會收到原始 JavaScript 堆疊追蹤字串、整個原始錯誤值以及所有傳遞給 REPL 的建置選項。然後,REPL 評估環境可以傳回規範的堆疊追蹤,其格式必須為

[{:function <string>
  :file <string>
  :line <integer>
  :column <integer>}*]

:file 必須是相對於 :output-dir 的沒有 URI 協定的 URL 樣式路徑(正斜線)。

透過此提交,合約已稍微放寬以容納 REPL 定義的函式::file 值可以以 < 開頭,以表示不存在原始碼,且追蹤中將會發出 "NO_SOURCE_FILE"

自訂 REPL 可能仍然想要進一步自訂或控制堆疊追蹤的列印。提供了一個掛鉤,REPL 評估環境可以實作 cljs.repl/IPrintStacktrace-print-stacktrace 採用對應的規範堆疊追蹤、整個原始錯誤值以及所有傳遞給 REPL 的建置選項。

原始作者:David Nolen