Инструментарий разработчика

Shadow-ClJS

REPL в браузере хорош для первого знакомства с языком, однако с его помощью не получится заниматься непосредственно разработкой. Чтобы разрабатывать реальные проекты на ClojureScript, вам потребуются установленные на вашей ваш компьютер компилятор ClojureScript в JavaScript, инструмент для управления зависимостями (сторонними библиотеками), REPL.

Сейчас все эти задачи берёт на себя инструмент под названием Shadow-ClJS. Вы можете найти этот проект по ссылке: https://github.com/thheller/shadow-cljs Самый простой способ установки shadow-cljs, который при этом подходит для большинства пользователей — это генерация проекта с помощью следующей команды:

$ npx create-cljs-project hello
...

Эта команда создаст директорию с именем "hello" и заполнит необходимым минимумом файлов, среди которых наиболее интересен shadow-cljs.edn — основной файл настройки того, как будет компилироваться ваш проект.

После того, как npx закончит подготавливать проект, вы сможете запустить REPL.

REPL

Компилятор ClojureScript сам по себе является программой, написанной на Clojure и запускается на Java Virtual Machine. Но код на языке JavaScript, которые появляется в результате компиляции ClojureScript­кода, тоже нужно на чём-то запускать — нужна среда исполнения JavaScript. Таковых обычно имеется две на выбор: NodeJS и Web-браузер. REPL, использующий NodeJS для выполнения JavaScript, запускается командой

$ npx shadow-cljs node-repl

Этот REPL максимально похож на тот, что вы видели на странице Try.Clojure. Вы можете вводить код и видеть результат его выполнения в вашем окне терминала.

REPL, исполняющий JS силами Web­браузера, запускается командой

$ npx shadow-cljs browser-repl
shadow-cljs - config: /home/astynax/Projects/cljs/hello/shadow-cljs.edn
shadow-cljs - server version: 2.20.17 running at http://localhost:9630
shadow-cljs - nREPL server started on port 37463
[:browser-repl] Configuring build.
[:browser-repl] Compiling ...

После того, как вы запустите эту команду, через какое-то время ваш браузер откроет страницу с текстом "Code entered in a browser-repl prompt will be evaluated here.". При этом в окне терминала вы всё так же будете видеть так называемое приглашение "cljs.user=>" и сможете вводить код.

Если окно браузера на открывается автоматически, то вы можете вручную открыть в браузере адрес http://localhost:9630 (номер порта может отличаться, нужный вам вы найдёте среди сообщений, выводимых в терминале — ищите строчку с текстом "server … running at …").

Browser REPL является основным, когда вы разрабатываете Web­приложения для браузера. При этом вам доступны многие привычные для Web­разработчика функции, только видите вы их в обёртке ClojureScript. Например, если ввести в ответ на приглашение строчку (js/alert "Hello!"), то браузер покажет модальное окно с соответствующим сообщением. А если ввести (.log js/console "ping"), то сообщение уже будет выведено в консоль инструментария разработчика — опять же, в окне браузера.

Первый код

Сгенерированный проект содержит поддиректории src/main и src/test, в которых и принято размещать код и тесты к нему. Код на языке Clojure хранится в файлах, каждый из которых описывает пространство имён (в других языках эти файлы могут называется "модулями"), при этом и файлы, и пространства имён называются в соответствии с соглашением Java Naming Conventions. И как бы нам не хотелось назвать файл просто app.cljs, следует придумать более уникальное и "говорящее" имя вида "company.project.name".

Создайте файл src/main/com/frontend/app.cljs со следующим содержимым:

(ns com.frontend.app)

(defn init []
  (js/alert "Hello World!"))

Чтобы компилятор смог найти этот файл и использовать функцию init как точку входа в программу (вызывать эту функцию при показе Web­страницы), нужно модифицировать shadow-cljs.edn. Нужно изменить значение ключа :builds так, чтобы оно выглядело следующим образом:

{
...

:builds
{:frontend
  {:target :browser
   :modules {:main {:init-fn com.frontend.app/init}}
   }}
}

Компилятор помещает полученный JavaScript­код в файл publis/js/main.js. А чтобы браузер мог этот файл загрузить, вам понадобится HTML­страница public/index.html следующего вида:

<!doctype html>
<html>
  <body>
    <div id="root"></div>
    <script src="/js/main.js"></script>
  </body>
</html>

И, наконец, нужно включить HTTP­server, встроенный в Shadow-ClJS, чтобы тот мог отдавать браузеру файлы. В shadow-cljs.end нужно добавить следующее:

{
...
:dev-http {8080 "public"} ; <- порт и директория

:builds
{...}

}

Теперь, если вы запустите shadow-cljs в "режиме разработки" командой

$ npx shadow-cljs watch frontend
...
shadow-cljs - HTTP server available at http://localhost:8080

Теперь компилятор будет собирать новый JS­файл в ответ на каждое изменение ClojureScript­кода, после чего останется только обновить страницу в браузере! А если вы откроете в браузере адрес http://localhost:8080 то будет вызвана та самая функция init и браузер покажет модальное окно с сообщением.

Важное свойство "режима разработки" заключается в том, что каждое изменение кода после компиляции оного (успешной, разумеется) загружается в браузер автоматически. Это обычно означает, что вам не нужно перезагружать страницу каждый раз, когда вы вносите изменения в код на ClojureScript. Более того, есть способы сохранить текущее состояние приложения, даже если логика его работы периодически меняется!

server, watch, cljs-repl

Режим "watch", он же "режим разработки" всем хорош, кроме того, что вы не имеете возможность коммуницировать с REPL, а можете только редактировать код, который затем загружается в браузер. Но иногда всё же хочется иметь возможность повзаимодействовать с приложением в интерактивном режиме.

Часто такую возможность даёт ваш редактор — если он умеет работать с Clojure и ClojureScript. Для таких редакторов Shadow ClJS предоставляет доступ к так называемому протоколу "nREPL", поэтому при старте shadow-cljs вы видите сообщение вида "nREPL server started on port …". Если же вам хочется запускать REPL вручную из терминала, лучше сразу запускать shadow-cljs в режиме сервера командой

$ npx shadow-cljs server frontend
...
shadow-cljs - HTTP server available at http://localhost:8000
shadow-cljs - server version: 2.20.17 running at http://localhost:9630
shadow-cljs - nREPL server started on port 43035

HTTP­server будет запущен, так что открыть страницу в браузере вы сможете. Но горячая загрузка кода пока не будет работать.

Теперь, уже в другом окне терминала вам нужно запустить ещё один экземпляр Shadow в режиме watch и этот новый экземпляр подключится к уже запущенному серверу:

$ npx shadow-cljs watch frontend
...
shadow-cljs - connected to server
shadow-cljs - watching build :frontend

В этот момент заработает горячая перезагрузка кода.

Чтобы запустить REPL, вам потребуется третье окно терминала, в котором следует вызвать команду:

$ npx shadow-cljs cljs-repl frontend
...
shadow-cljs - connected to server
cljs.user=>

Вот и REPL, который работает в связке с браузером, а значит можно с оным пообщаться:

cljs.user=> (js/alert "Ping!")
nil

Чтобы получить доступ к коду, который мы написали в модуле app.cljs, вам нужно в REPL "переключиться" на соответствующее пространство имён — то, которое мы указали в форме (ns ...):

cljs.user=> (in-ns 'com.frontend.app)  ;; имя процитировано!
com.frontend.app=> (init)  ;; теперь функция init видна
nil

Узнайте, как в вашем любимом редакторе обстоят дела с поддержкой Clojure nREPL. Многие редакторы позволяют подключиться к nREPL server напрямую, так что отдельно запускать REPL вам не придётся. Однако знать о такой возможности полезно, поэтому она здесь и упомянута.