Предисловие
В данной статье я поделюсь с вами опытом, как быстро и безболезненно создавать простые плагины для gulp. Статья ориентирована на таких же чайников, как и я. На тех, кто до сих пор лишь использовал готовые плоды gulp, срывая их с великого Древа Познания NPM, и не имел серьезного опыта работы с Node JS и его стримами.
Я не буду отвечать на вопросы вида «а зачем создавать свои плагины, если уже написано все, что только возможно?». Придет время и вам за пол часа нужно будет написать что-то очень специфичное для вашего проекта. Перерыв весь npm, вы найдете один заброшенный плагин с убогим функционалом, автор которого недоступен, код ужасен и так далее. А может быть это будет настолько специфичная задача, что вы не найдете абсолютно ничего.
Такой задачей для меня стала визуализация большого проекта, использующего Angular JS. Было огромное количество angular-модулей, связи между которыми были для меня уже не столь очевидными. Плюс мне хотелось видеть «диверсантов» — модули, которые каким-либо образом нарушали общую концепцию проекта (например, лезли в другой модуль не через провайдера, а напрямую).
Поискав, я нашел такое решение своей задачи. В принципе, запускать grunt плагины в gulp достаточно просто, но реализация в этом плагине меня не слишком впечатлила. Мне не хотелось использовать сторонние программы, а именно graphviz в качестве средства визуализации графа. Плюс ко всему, кто знает, что мне потребуется еще, а зависимость от сторонних библиотек всегда налагает ограничения.
Если читателя интересует лишь этот плагин, а не сама статья, то вот ссылка на проект на github и на npm. Всем остальным — добро пожаловать под кат.
С чего начать?
Gulp-разработчики любезно помогают нам в наших начинаниях, создав вики-документацию для начинающих разработчиков плагинов здесь. Для успешной разработки достаточно прочитать титульник и гайдлайны. Можно обойтись и без последних, но если в будущем вы планируете выкладывать свой модуль в публичный npm, то чтобы не собирать кирпичи на свою голову, советую не проходить мимо гайдлайнов.
Краткий конспект философии gulp-плагинов:
- ваш плагин всегда принимает набор Vinyl объектов
- ваш плагин всегда должен отдавать набор Vinyl объектов (вы можете этого и не делать, но с результатом вашего плагина потом невозможно будет работать другим плагинам. Это обязательно выстрелит)
- что за винил? Vinyl file object — в простонародье просто файл. В свойстве path хранит filename — полный путь до файла, в свойстве contents — буфер или стрим с содержанием файла
- никогда не пишите плагины, которые будут делать то же самое, что и существующие node пакеты. Вы попадете в блэклист. И вполне справедливо
Плюс ко всему разработчики советуют ознакомиться с хорошо написанными простыми плагинами. Я бы советовал посмотреть на код gulp-replace
Реализуем свои идеи
Я приведу наиболее устоявшийся шаблон построения gulp-плагинов, который используется в большинстве хороших плагинов. Детальное описание реализации моей задачи — не есть цель данной статьи. Основная цель в том, чтобы каждый мог быстро «въехать» на примере и пойти создавать свой плагин.
Итак, начнём. Предполагается, что node js уже стоит в системе глобально.
npm init
Main файл проекта пусть будет index.js. После заполнения основной информации, устанавливаем следующее
npm install --save through2 gulp-util vinyl
Первый плагин значительно упростит обработку vinyl-стримов. Второй пригодится для генерации ошибок плагином. Третий пригодится, если вы будете создавать новые vinyl-файлы, на основе входных файлов. В моей задаче он пригодится.
Займемся проектированием. Итак, я хочу получить два файла. Первый — это описание графа в формате dot для поддержки graphviz, если вдруг что. Второй — это html файл, открыв который я увижу красивый граф, нарисованный с помощью d3. Итого в моей задаче есть 3 основных действия:
- получить массив всех ангуляр-модулей объявленных в файлах, которые примет в себя плагин
- создать .dot файл графа на основе массива модулей
- создать визуальное представление графа (html файл c d3-скриптом)
Создаем index.js, чистый как холст, и бросаем на него побольше красок:
var through = require('through2'), gutil = require('gulp-util'), //ты будешь извлекать массив ангуляр-модулей ModulesReader = require('./lib/modules-reader'), //ты будешь строить граф GraphBuilder = require('./lib/graph-builder'), //а ты его визуализировать GraphVisualizer = require('./lib/graph-visualizer'); //экспортируем функцию, вызывая которую в тасках gulp, пользователь инициирует наш плагин module.exports = function(options) { //#section инициализация var modulesReader; var graphBuilder; var graphVisualizer; options = options || {}; if (!modulesReader) { modulesReader = new ModulesReader(); } if (!graphBuilder) { graphBuilder = new GraphBuilder(); } if (!graphVisualizer) { graphVisualizer = new GraphVisualizer(); } //#endsection инициализация //функция, которую будет вызывать through для каждого файла function bufferContents(file, enc, callback) { if (file.isStream()) { //бросим ошибку с помощью gulp-util this.emit('error', new gutil.PluginError('gulp-ng-graph', 'Streams are not supported!')); return callback(); } if (file.isBuffer()) { //отдадим файл на чтение нашему читателю модулей ангуляра modulesReader.read(file.contents, enc); } callback(); } //функция вызывающаяся перед закрытием стрима function endStream(callback) { var modules = modulesReader.getModules(); if (!modules || !modules.length) { return; } //соберем dot файл и объект графа var builderData = graphBuilder.build({ modules: modules, dot: options.dot || 'ng-graph.dot', }); //соберем html файл на основе объекта графа var htmlFile = graphVisualizer.render({ graph: builderData.graph, html: options.html || 'ng-graph.html', }); //отправляем результат в стрим this.push(builderData.dotFile); this.push(htmlFile); callback(); } return through.obj(bufferContents, endStream); };
Важно помнить, что если вы планируете возвращать обработанные входные файлы, то необходимо в функции bufferContents вызывать this.push(file) после манипуляций с контентом файла. Но если вы планируете (как в моей задаче) генерировать новые файлы на основе входных, то вам обязательно потребуется функция endStream, где стрим еще не закрыт и вы сможете добавить ваши файлы в пустой стрим.
Так как основная цель статьи — научиться писать плагины gulp на конкретном примере, то я не буду приводить здесь реализации ModulesReader, GraphBuilder и GraphVisualizaer, являющиеся специфичными для моей конкретной задачи. Если кого-то заинтересует их реализация, то добро пожаловать на гитхаб
Результат работы плагина — вот такой вот приятный граф проекта на d3 с возможностью зумирования.
ссылка на оригинал статьи http://habrahabr.ru/post/257275/
Добавить комментарий