ClojureScript

自定义 REPL

此页面记录了对使用 cljs.repl 中提供的功能的自定义 REPL 要求的最新更改。这些更改是为了实现大幅缩短所有 ClojureScript REPL 的启动时间,并简化 REPL 状态与编译源代码的同步。这是通过重用全局可用的编译缓存基础设施实现的。事实上,现在可以启动一个 REPL,并将:output-dir 设置为现有的编译缓存,从而无需进行分析或编译。

在新基础设施下,所有内置的 REPL 都能够在现代硬件上以一秒或更短的时间启动。

期望

为了尽可能快地启动 REPL,REPL 必须实现 -setup 的新的 2 个参数的函数,该函数接收典型的编译器构建选项。在过去,-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,而应该尊重 :print:print-no-newline:flushopts(第二个参数)中传递给 -setup 的关联值。还要注意,与 :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 的 URL 风格路径(正斜杠),不带 URI 协议。

随着 此提交,该契约已略微放宽,以适应 REPL 定义的函数::file 值可以以 < 开头,表示没有源代码,并且将在跟踪中发出 "NO_SOURCE_FILE"

自定义 REPL 仍然可能希望进一步自定义或控制堆栈跟踪的打印。提供了一个钩子,REPL 评估环境可以实现 cljs.repl/IPrintStacktrace-print-stacktrace 接受映射后的规范堆栈跟踪、完整的原始错误值,以及传递给 REPL 的所有构建选项。

原始作者:David Nolen