Monic — сборщик JS-файлов (форк Jossy) в один или несколько модулей. При правильном использовании позволяет не только легко собирать модули, но и также легко пересобирать их при изменении принципов сборки.
npm install monic --global
monic [options] [file ...]
-h, --help Вызов справки
-V, --version Вернуть версию Monic
-f, --file [string] Задать путь к исходному файлу (мета-информация)
-o, --output [string] Задать путь для сохранения сгенерированного файла
-m, --mode [string] Задать режим для создания папок
при сохранении сгенерированного файла
--eol [char] Задать разделитель строки (EOL)
--flags [list] Задать список флагов через запятую
--labels [list] Задать список меток через запятую
-s, --source-maps [string] [true|false|inline]
--source-map-file [string] Задать путь для сохранения SourceMap
--source-root [string] Задать корень для всех ссылок в SourceMap
Результат сборки выводится в output, поэтому для сохранения в файл нужно использовать возможности командной оболочки, например,
monic file.js --flags ie --labels escapeHTML > _file.js
Или можно использовать --output
monic file.js --flags ie --labels escapeHTML -o _file.js
Сборка файла с выводом результата в консоль
monic myFile.js
Сборка файла с сохранением результата в другой файл
monic myFile.js > myNewFile.js
Сборка файла с сохранением результата в другой файл и генерацией SourceMap
# SourceMap сохранится как "myFile-compiled.js.map"
monic myFile.js -s -o myFile-compiled.js
# SourceMap сохранится как "myFile.map"
monic myFile.js -s -o myFile-compiled.js --source-map myFile.map
# SourceMap сохранится внутри "myFile-compiled.js"
monic myFile.js -s inline -o myFile-compiled.js
Сборка текста с выводом результата в консоль
monic '//#include foo/*.js' -f myFile.js
Использование поверх stdio
echo '//#include foo/*.js' | monic -f myFile.js
var monic = require('monic');
monic.compile(
'myFile.js',
{
// Путь к рабочей директории
// (опционально, по умолчанию module.parent)
cwd: 'myDir/',
// Разделитель строки (опционально, по умолчанию \n)
eol: '\r\n',
// Таблица задаваемых меток (опционально)
labels: {
escapeHTML: true
},
// Таблица задаваемых флагов (опционально)
flags: {
ie: true
},
// Если true, то сгенерированные файлы будут сохранены
// (опционально, по умолчанию false)
saveFiles: true,
// Режим для создания папок при сохранении сгенерированного файла
// (опционально, по умолчанию 0777)
mode: '0666',
// Путь к сгенерированному файлу (опционально)
file: 'myFiled-compiled.js',
// Если true или 'inline', то будет сгенерирован source map
// (опционально, по умолчанию false)
sourceMaps: true,
// Объект source map на основе которого будет идти генерация
// (опционально)
inputSourceMap: null,
// Путь к сгенерированному source map (опционально, по умолчанию ${file}.map)
sourceMapFile: 'myFiled.map',
// Корень для всех ссылок в SourceMap (опционально)
sourceRoot: 'myDir/'
},
function (err, data, {map, decl, url, isExternal}) {
if (err) {
throw err;
}
console.log(data);
}
);
Использование Promise API (When)
var monic = require('monic');
monic.compile('myFile.js')
.then(function ([data, {map, decl, url, isExternal}]) {
...
})
.catch(function (err) {
...
});
var monic = require('monic');
monic.compile(
'myFile.js',
{
content: '...'
},
function (err, result) {
...
}
);
var monic = require('monic');
monic.compile(
'myFile.js',
{
replacers: [
// Замена require конструкций на #include
function (text, file) {
return text.replace(/^\s*require\('(.*?)'\);/gm, '//#include $1');
}
]
},
function (err, result) {
...
}
);
Включить содержимое внешнего файла в текущий можно директивой #include ...
.
//#include file.js
Путь к файлу указывается относительно расположения текущего файла или в абсолютной форме. В пути к файлу можно также использовать шаблоны.
//#include lib/*.js
Технически, вместо строки с директивой просто вставляется содержимое указанного файла. Однако, если указанный файл уже подключен в текущем модуле ранее, то повторно он включен не будет. Например,
f1.js
alert(1);
f2.js
//#include f1.js
alert(2);
f3.js
//#include f1.js
//#include f2.js
monic f3.js > result.js
result.js
alert(1);
alert(2);
Директива #without
указывает Monic исключить из сборки все файлы, которые используются в указанном
(включая указанный, разумеется).
Пример
В проекте есть несколько десятков виджетов. Код каждого виджета лежит в отдельном файле.
В каждом виджете указаны его зависимости с помощью директивы #include
.
Какие-то виджеты используются на большинстве страниц, и при сборке логично их код вынести в отдельный файл common.js.
Выбираем часто используемые виджеты, создаём файл common.js и пишем туда:
//#include widget1.js
//#include widget2.js
//#include widget3.js
На одной из страниц используется виджет, достаточно объёмный, чтобы не включать его в common.js, назовём его big-widget. В файле big-widget.js указаны его зависимости, среди которых, разумеется, много тех, которые уже есть в common.js. Если мы просто соберём файл big-widget.js, то получим много продублированного кода. Поэтому рядом с common.js создаём файл feature.js с содержимым:
//#without common.js
//#include big-widget.js
Теперь код, попавший в common.js, не попадёт в feature.js. Главное не забыть подключить на страницу не только feature.js, но и common.js.
Формат пути в директиве такой же, как и в #include
.
В процессе сборки можно определять специальные флаги, в зависимости от которых выводить или не выводить строки кода.
//#set flag
//#if flag
alert('flag');
/*? Можно использовать //#end if */
//#endif
//#unset flag
//#unless flag
alert('not flag');
/*? Можно использовать //#end unless */
//#endunless
Флагам можно задавать значения.
//#set ie 7
//#if ie 7
alert('OMG!');
//#endif
//#unless ie 7
alert('Cool!');
//#endunless
Директива #match
предоставляет самый гибкий интерфейс для работы с условиями.
//#set foo
//#match foo
alert('foo');
/*? Можно использовать //#end match */
//#endmatch
//#unset foo
//#match foo !=
alert('foo !=');
//#endmatch
//#set ie 7
//#match ie = 7
alert('ie = 7');
//#endmatch
//#match ie != 8
alert('ie != 8');
//#endmatch
//#match ie > 6
alert('ie > 6');
//#endmatch
//#match ie >= 7
alert('ie >= 7');
//#endmatch
//#match ie < 8
alert('ie < 8');
//#endmatch
//#match ie <= 7
alert('ie <= 7');
//#endmatch
Флаги глобальные. Указать их можно не только в коде директивами #set
и #unset
, но при запуске сборщика. Например,
file.js
//#if ie
alert('IE only');
//#endif
common.js
//#include file.js
common-ie.js
//#set ie
//#include file.js
Точно также можно создать флаг debug и писать отладочные строки только внутри //#if debug ... //#endif
,
тогда отладочный код никогда не попадёт на боевые сервера.
Флаги, которые были заданы при запуске сборщика или в глобальной области файла, могут быть вызваны в директивах
#include
и #without
через специальный синтаксис.
//#set lang en
//#include lang/${lang}.json
Если заданного флага не существует, то вставится пустая строка.
Эта функциональность очень полезна полезна при разработке библиотек и фреймворков. Например, в нашей библиотеке есть файл String.js, содержащий несколько десятков функций для работы со строками. Выделять каждую функцию в отдельный файл как-то неправильно, но и подключать потом несколько сотен строк кода ради одной функции тоже не хочется. В случае с Monic файл String.js размечается на области. Имена у областей могут быть произвольными, но лучше, чтобы они совпадали с именами функций.
var String = {};
//#label truncate
String.truncate = function () {
};
/*? Можно использовать //#end label */
//#endlabel truncate
//#label escapeHTML
String.escapeHTML = function () {
};
//#endlabel escapeHTML
Теперь, если нам нужна только функция escapeHTML
, то при подключении файла String.js пишем
//#include String.js::escapeHTML
В результате в сборку попадёт только
var String = {};
String.escapeHTML = function () {
};
Если нужно подключить несколько областей, указываем несколько
//#include String.js::trim::truncate
Если нужно подключить всё, кроме размеченных областей (например, нам нужен только namespace String), то
//#include String.js::
Если же какой-то области необходима другая область из текущего файла, то используем #include
без указания файла.
//#label truncate
//#include ::trim
String.truncate = function () {};
//#endlabel truncate
Обратите внимание, что размеченные таким образом области файла в собранном коде могут поменять порядок и между ними может появиться другой код.
Например,
//#include String.js::escapeHTML
alert(1);
//#include String.js::truncate
После сборки получим
var String = {};
String.escapeHTML = function () {
};
alert(1);
String.truncate = function () {
};
Поэтому использовать #label
внутри функций и выражений нельзя, на выходе получим поломанный JavaScript.
Кроме этого, #without
тоже смотрит на эти области. Поэтому, например, escapeHTML
может попасть в common.js,
а truncate
— в feature.js.
The MIT License.