Давненько у меня был pet-project - find, идея которого до боли проста, — трекинг людей на карте. Человек заходит на сайт, вводит свое и имя комнаты, дает ссылку на комнату другу и они могут смотреть кто где находится на карте.
Тогда для этой поделки решил использовать React и Erlang для тренировки. Все было замечательно, пока я не столкнулся с проблемой - компиляция ассетов, хотелось чего-то на подобии Rails Asset Pipeline и Sprockets, но ничего тогда не нашел.
Проект на долгое время ушел в большой ящик. Совсем недавно я его оттуда достал и сразу начал решать проблему с компиляцией статических файлов. Нужен был удобный клон Assets из Rails. За ним далеко ходить не пришлось — подошел require из node.js и плагин browserify. При этом надо было уметь компилировать React с его JSX и скукоживать в один файл. С CSS все проще — понадобится только один плагин — node-sass, который умеет компилировать и сжимать.
Rebar
У этой утилиты есть хуки, которые запускаются до или после компиляции приложения. Чтобы не вешать все на него, я решил просто выполнять make build-static
из корня Erlang приложения:
% rebar.config
{post_hooks, [{compile, "make build-static"}]}.
NodeJS
Т.к. я использую систему модулей из nodejs, то можно использовать его как менеджер зависимостей, тем более что он стал практически стандартом де-факто для подобного рода манипуляций:
// package.json
{
"dependencies": {
"uglify-js": "~ 2.4.15",
"react": "~ 0.11.1",
"react-tools": "~ 0.11.1",
"browserify": "~ 5.11.0",
"node-sass": "~ 0.9.3"
}
}
Makefile
Мой makefile до боли прост:
PATH := node_modules/.bin:$(PATH)
build-static: notify clean npm-install jsx browserify uglify-js sass clean
notify:
@echo "Compile static files"
clean:
@rm -rf priv/temp
sass:
@node-sass priv/source/css/** --output-style compressed -o priv/static/app.css > /dev/null 2>&1
jsx:
@jsx priv/source/js priv/temp/js > /dev/null 2>&1
browserify:
@browserify priv/temp/js/app.js > priv/temp/js/bundle.js
uglify-js:
@uglifyjs priv/temp/js/bundle.js > priv/static/app.js 2> /dev/null
npm-install:
@npm install > /dev/null 2>&1
Можно, конечно, убрать все > /dev/null 2>&1
, чтобы было видно ошибки в случае чего, но я не хочу засорять и без того слишком информативный лог компиляции от rebar, да и почти все модули не умееют скрывать дебаг информацию.
Структура приложения
.
├── makefile
├── package.json
├── rebar.config
├── priv
│ ├── source
│ │ ├── css
│ │ │ ├── app.sass
│ │ │ └── components
│ │ │ └── hello.sass
│ │ └── js
│ │ ├── app.js
│ │ └── components
│ │ └── hello.js
│ ├── static
│ │ ├── app.css
│ │ ├── app.js
│ │ ├── css
│ │ │ └── normalize.css
│ │ ├── images
│ │ └── js
│ │ ├── bullet.js
│ │ ├── store.js
│ │ └── utils.js
└── src
├── dummy.app.src
├── dummy.erl
├── dummy_app.erl
└── dummy_sup.erl
Тут все просто, сначала компилируем jsx из priv/source/js
и складываем результат в priv/temp/js
, затем все файлы из priv/temp/js
упаковываем с помощью browserify в bundle.js, скармливаем этот файл uglifyjs и кладем результат в priv/static/app.js
. Весь процесс проходит достаточно быстро.
Организация JS
// priv/source/js/app.js
/** @jsx React.DOM */
var React = require('react'),
Hello = require('./components/hello.js');
React.renderComponent(
<Hello />, document.body
);
// priv/source/js/components/hello.js
/** @jsx React.DOM */
var React = require('react');
module.exports = React.createClass({
render: function() {
return (
<div id="hello">Hello, world!</div>
);
}
});
Организация CSS
// priv/source/css/app.sass
@import "components/hello"
html, body
font-family: Arial, sans-serif
font-size: 1em
// priv/source/css/components/hello.sass
#hello
margin: 20px
Cowboy
Скомпилированную статику надо скормить веб-серверу:
cowboy_router:compile([
{'_', [ {"/", index_handler, []}
, {"/static/[...]", cowboy_static, {priv_dir, dummy, <<"static">>, []}}
]}
]).
Вот примерно так я работаю со статикой. Все это кажется большим костылем, я бы взял и написал что-то типа Sprockets для Erlang в виде плагина для Rebar, но времени на это совсем нет, да и оно никому не надо.
Хотя можно было бы сделать что-то очень простое, чтобы смотрело в директории с исходниками статики, компилировало по правилам и выводила ошибки, если те вдруг возникнут. Может есть у кого идеи или наброски?