Как создать независимую библиотеку 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, преобразованный в JavaScript
  • shadow-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. Это потрясающий инструмент.

Другие вопросы по тегам