如何使用ClojureScript编写与作者无关的JavaScript库?

5
假设我有一个包含以下内容的 cljs 文件:
(ns foo)
(defn add [x y]
  (+ x y))

我希望将此作为JavaScript库提供给非ClojureScript开发人员(主要针对node.js)。以下是我的方法:

clj -m cljs.main -c foo

但问题在于输出是针对 google closure 的模块系统(例如 goog.require)。我可以使用 -t 标志将目标设置为 none(而不是浏览器或节点),但这并没有解决这个问题。将其设置为 node 也无法解决此问题:没有 index.js(它的命名方式类似于 Java 中的主文件),也没有 module.exports = blah blah。看起来它是针对独立的完整的 node 应用程序,而不是库。
我知道 ClojureScript 使用 google closure 进行自己的子模块,并且我不一定希望摆脱所有这些(我不确定你是否能够)。而且我知道 es2015 原生 JavaScript 模块由于其静态性质已经过时。
我可以通过手动或脚本调整输出使其与 npm 生态系统相兼容,但我很惊讶居然没有编译器选项可以实际输出一个符合 npm 规范的模块。还是说有吗?难道我只是误读了 --help 吗?
2个回答

5
这里假设您已经成功安装了ClojureScript和Node.js。
给定:
math101
|-- package.json
|-- src
|   `-- com
|       `-- example
|           `-- math.cljs

package.json

{
  "name": "math101",
  "version": "1.0.0",
  "main": "dist/index.js",
  "license": "MIT",
  "devDependencies": {
    "shadow-cljs": "^2.8.52",
    "source-map-support": "^0.5.13"
  }
}

注意:

在继续之前,请确保您已安装这两个NPM依赖项。

math.cljs

(ns com.example.math)

(defn add [x y]
  (+ x y))

1. 设置构建工具

math101 的根目录中运行 yarn shadow-cljs init
这将创建一个名为 shadow-cljs.edn 的文件,其中包含一些默认设置:

;; shadow-cljs configuration
{:source-paths
 ["src/dev"
  "src/main"
  "src/test"]

 :dependencies
 []

 :builds
 {}}

让我们做出一些改变。

首先,您不需要这么多源路径:

{:source-paths
 ["src"]

 :dependencies
 []

 :builds
 {}}

那我们来新增一个构建配置:
;; shadow-cljs configuration
{:source-paths
 ["src"]

 :dependencies
 []

 :builds
 {:math101 {:target :node-library
            :output-to "dist/index.js"
            :exports-var com.example.math/add}}}

注意:

  • :math101 - 这是我们稍后将使用的构建 ID
  • :target :node-library - 这告诉 shadow-cljs 您打算编写一个库
  • :output-to "dist/index.js" - 您打算发布的代码
  • :exports-var com.example.math/add - 您打算发布的函数的“完全限定名称”。这将产生一个默认导出。

其他说明:

:target :node-library 发出可以通过 require 用作标准节点库的代码,并且对于发布您的代码以用作编译后的 Javascript 工件非常有用。

来源

有一个可用的 :npm-module 目标,但到目前为止,:node-library 已经满足了我所有的需求。

2. 让我们构建它!

运行 yarn shadow-cljs compile math101
第一次运行此命令时,shadow-cljs 将下载一堆东西。最终完成后,...

$ node
> var add = require('./dist')
> add(40, 2)
42

✨✨✨

需要导出不止一个函数吗?没问题。

我们来添加subtract

(ns com.example.math)

(defn add [x y]
  (+ x y))

(defn subtract [x y]
  (- x y))

现在让我们更新构建配置:

;; shadow-cljs configuration
{:source-paths
 ["src"]

 :dependencies
 []

 :builds
 {:math101 {:target :node-library
            :output-to "dist/index.js"
            :exports {:add com.example.math/add
                      :subtract com.example.math/subtract}}}}

再次运行yarn shadow-cljs compile math101,等它完成后:
$ node
> var math101 = require('./dist')
> math101.add(40, 2)
42
> math101.subtract(44, 2)
42

✨✨✨✨✨✨


补充说明

这只是为了让你入门而已。shadow-cljs compile会生成非生产代码(即未压缩的,我不确定是否删除了无用代码,并且Google Closure还没有运行任何优化)。

生成生产就绪代码的命令是shadow-cljs release,但您可能需要在构建配置之前进行微调。

我强烈建议您投入时间阅读shadow-cljs文档。这是一个很棒的工具。


非常感谢您提供的详细解释,您帮了我很大的忙。 - Takis

4

shadow-cljs支持通过 :target :npm-module 以CommonJS格式输出,这确实支持了您所要求的内容。 Node和其他JS工具(例如webpack)可以独立使用不同的命名空间。默认CLJS工具不支持此模式。

然而,ClojureScript非常适合假设整个程序将由Closure Compiler进行优化。 这使得它不太适合编写要包含在其他构建中的库。 以这种方式构建的每个“库”都将包含自己的版本cljs.core,因此一开始就会非常庞大,并且包含2个以这种方式构建的库会导致灾难,因为它们彼此不兼容。


我并不太关心大小(当然,所有事情都相等的话,越小越好)。我希望使用ClojureScript作为编写Atom编辑器插件的更好语言,但听起来存在太多摩擦。 - Jared Smith
如果代码大小不是主要考虑因素,那么摩擦力非常小。除非您打算编写许多直接相互交互的插件,否则这不是问题。 - Thomas Heller
1
我想我只能试一试,看看它的效果如何。 - Jared Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接