Как создать независимую библиотеку JavaScript в ClojureScript?
Допустим, у меня есть cljs
файл, содержащий следующее:
(ns foo)
(defn add [x y]
(+ x y))
и хотим сделать это доступным в виде библиотеки JavaScript для разработчиков, не являющихся ClojureScript (в первую очередь ориентированных на node.js). Я могу сделать это:
clj -m cljs.main -c foo
Но проблема в том, что вывод ориентирован на систему модулей Google Closure (например, goog.require
). Я могу установить цель на none
с -t
флаг (в отличие от браузера или узла), и это... не исправляет это. Установка его в node
также не решает проблему: нет index.js
(это называется main как в Java), нет module.exports = blah blah
, Похоже, что он предназначен для автономных приложений с полным узлом, а не для библиотек.
Я понимаю, что ClojureScript использует закрытие Google для своих собственных подмодулей, и я не обязательно ищу избавления от всего этого (я не уверен, что вы могли бы). И я понимаю, что встроенные модули es2015 JavaScript отсутствуют из-за их статической природы.
Я мог бы помассировать вывод вручную или скриптом, чтобы играть хорошо с экосистемой npm, но я удивлен, что нет опции компилятора, которая могла бы на самом деле вывести модуль, дружественный к npm. Или есть? Я просто читаю --help
неправильно?
1 ответ
shadow-cljs поддерживает вывод в формате CommonJS через :target:npm-module, который поддерживает именно то, что вы просите. Node и другие инструменты JS (например, webpack
) может потреблять отдельные пространства имен независимо. Инструменты CLJS по умолчанию не поддерживают этот режим.
ClojureScript, тем не менее, очень написан с предположением, что вся ваша программа будет оптимизирована компилятором Closure. Это делает его менее чем идеальным для написания библиотек для включения в другие сборки. Каждая "библиотека", построенная таким образом, будет содержать свою собственную версию cljs.core
и поэтому будет довольно большим для начала, и включение 2-х библиотек, построенных таким образом, является рецептом для катастрофы, так как они не будут совместимы друг с другом.
Предполагается, что у вас уже есть рабочая установка 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"
}
}
Заметки:
dist/index.js
- Это будет содержать наш код ClojureScript, преобразованный в JavaScriptshadow-cljs
- Инструмент сборки (и управления зависимостями), который нам нуженsource-map-support
- Требуется для запуска ClojureScript на Node.js
Перед тем как продолжить, убедитесь, что вы установили эти две зависимости 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
- Это идентификатор сборки, который мы будем использовать позже:target :node-library
- Это говоритshadow-cljs
что вы собираетесь создать библиотеку:output-to "dist/index.js"
- Код, который вы собираетесь опубликовать:exports-var com.example.math/add
- "Полное имя" функции, которую вы собираетесь опубликовать. Это произведет экспорт по умолчанию.
Дополнительные примечания:
: Target: node-library испускает код, который можно использовать (с помощью require) в качестве стандартной библиотеки узлов, и полезен для публикации вашего кода для повторного использования в качестве скомпилированного артефакта Javascript.
E сть :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
генерирует непроизводственный код (т.е. он не минифицирован, мертвый код не удаляется AFAIK, а Google Closure еще не выполнял оптимизацию).
Команда для генерации готового кода: shadow-cljs release
но вы можете захотеть настроить конфигурацию сборки раньше.
Я настоятельно рекомендую вам потратить время на чтение документации shadow-cljs. Это потрясающий инструмент.