Приятная сборка frontend проекта. Разбираемся со сборкой front end на Gulp. Альтернативы gulp и grunt
В последнее время Gulp набирает большую популярность, и понятно почему. Он быстрее, красивее и проще чем Grunt . Мне приходилось часто с ним работать, но я всегда брал готовые решения и не до конца понимал как же он все это делает. На этих выходных я решил разобрать и закрыть эту небольшую проблему. Об этом и поговорим сегодня.
Что такое Gulp?
Gulp - это инструмент сборки front-a. Он позволяет автоматизировать повторяющиеся задачи (сборка и минификация CSS- и JS-файлов, запуск тестов, перезагрузка браузера и другие). Тем самым Gulp ускоряет и оптимизирует процесс веб-разработки.
Установка Gulp
Установить Gulp достаточно легко. Если у вас что-то не получится, пишите в комментариях или загуглите вашу проблему. Итак для установки нужно сделать 3 шага:
- Установить Gulp глобально
- Установить Gulp как devDependencies (зависимости для разработки)
- Создать файл gulpfile.js
Первый шаг - устанавливаем глобально Gulp. Открываем терминал и пишем:
npm install --global gulp
После этого вам нужно установить Gulp как devDependencies для вашего проекта. Убедитесь в том, что у вас есть файл package.json . Если его нет, то создайте его написав в консоль npm init . Теперь можно установить Gulp как devDependencies:
npm install --save-dev gulp
И наконец, вам нужно создать gulpfile.js в корне вашего проекта, который будет содержать ваши задачи (tasks). В качестве промежуточного шага, мы установим плагин gulp-util . Чтобы показать как устанавливаются плагины:
npm install --save-dev gulp-util
Теперь настало время написать нашу первую задачку. Открываем только что созданный файл gulpfile.js и пишем в него следующее:
/* File: gulpfile.js */ // собираем все наши плагины var gulp = require (" gulp " ), gutil = require (" gulp-util " ); // создаем задачку, которая будет выполняться по умолчанию gulp . task (" default " , function () { return gutil . log (" Gulp is running! " ) });
И теперь нам остается запустить gulp в терминале и мы увидим нечто похожее на это:
> gulp [ 12:32:08] Using gulpfile ~/Projects/gulp-scotch-io/gulpfile.js [ 12:32:08] Starting "default" ... [ 12:32:08] Gulp is running! [ 12:32:08] Finished "default" after 1 ms
Обзор
Сам по себе Gulp очень скуден на возможности. Но все, что вам нужно вынесено в отдельные плагины. Они совместно с Gulp творят чудеса.
Api у gulp очень маленькое, и содержит всего 4 функции высшего порядка:
- gulp.task
- gulp.src
- gulp.dest
- gulp.watch
gulp.task определяет наши задачи. В качестве аргументов принимает название, зависимости (массив) и функцию (основные действия). Зависимостей может и не быть:
gulp . task (" mytask " , function () { //сделать что-то }); gulp . task (" dependenttask " , [ " mytask " ], function () { //сделать что-то после того, как "mytask" будет выполнен });
gulp.src указывает на файлы, которые мы хотим использовать. Он использует.pipe доступа к файлам через плагины.
gulp.dest указывает на папку, в которую мы хотим сохранить измененные файлы.
gulp.src и gulp.dest используется для простой копии файлов:
gulp . task (" copyHtml " , function () { // скопировать все html файлы из source/ в public/ gulp . src (" source/*.html " ). pipe (gulp . dest (" public " )); });
В gulp встроена система реагирования на изменения файлов (gulp.watch). Вы можете использовать эту задачу для запуска других необходимых вам задач при изменении файлов.
Сжатие изображение, JS и CSS файлов, в целях оптимизации загрузки веб-страниц и многоe многое другое. Чтобы упросить этот процесс, мы предлагаем вам воспользоваться сборкой проектов Gulp 4, которую постоянно совершенствует Андрей Горохов. Ниже будут представлены ссылки на скачивание, а пока пройдемся по основным моментам: описанию и установке.
Сборщик проектов Gulp
Gulp - это сборщик проектов, инструмент для автоматизации задач, которые описаны выше. Он поможет вам ускорить вашу работу и грамотно подготовить проект к релизу.
Скачать сборку можно с репозитория Github или через командную строку Git . В дальнейшем вы сможете настроить её под свои задачи.
Особенности
- именование классов по БЭМ
- используется БЭМ-структура
- используется препроцессор SCSS
- используется транспайлер Babel для поддержки современного JavaScript (ES6) в браузерах
- используется Webpack для сборки JavaScript-модулей
- используется CSS-сетка smart-grid на основе Bootstrap для быстрой адаптивной вёрстки
- используется жёсткий кодгайд
Установка
- установите NodeJS (если требуется) и Yarn
- скачайте сборку с помощью Git : git clone https://github.com/andreyalexeich/gulp-scss-starter.git
- установите gulp глобально: yarn global add gulp-cli
- перейдите в скачанную папку со сборкой: cd gulp-scss-starter
- скачайте необходимые зависимости: yarn
- чтобы начать работу, введите команду: yarn run dev (режим разработки)
- чтобы собрать проект, введите команду yarn run build (режим сборки)
Если вы всё сделали правильно, у вас должен открыться браузер с локальным сервером. Режим сборки предполагает оптимизацию проекта: сжатие изображений, минифицирование CSS и JS-файлов для загрузки на сервер.
Если у тебя возникли проблемы с установкой, то посмотри этот ролик:
Файловая структура
gulp-scss-starter ├── dist ├── gulp-tasks ├── src │ ├── blocks │ ├── fonts │ ├── img │ ├── js │ ├── styles │ ├── views │ └── .htaccess ├── gulpfile.babel.js ├── webpack.config.js ├── package.json ├── .babelrc.js ├── .bemrc.js ├── .eslintrc.json ├── .stylelintrc ├── .stylelintignore └── .gitignore- Корень папки:
- .babelrc.js - настройки Babel
- .bemrc.js - настройки БЭМ
- .eslintrc.json - настройки ESLint
- .gitignore – запрет на отслеживание файлов Git’ом
- .stylelintrc - настройки Stylelint
- .stylelintignore – запрет на отслеживание файлов Stylelint’ом
- gulpfile.babel.js - настройки Gulp
- webpack.config.js - настройки Webpack
- package.json - список зависимостей
- Папка src — используется во время разработки:
- БЭМ-блоки: src/blocks
- шрифты: src/fonts
- изображения: src/img
- JS-файлы: src/js
- страницы сайта: src/views/pages
- SCSS-файлы: src/styles
- HTML-файлы: src/views
- конфигурационный файл веб-сервера Apache с настройками gzip (сжатие без потерь): src/.htaccess
- Папка dist — папка, из которой запускается локальный сервер для разработки (при запуске yarn run dev)
- Папка gulp-tasks — папка с Gulp-тасками
Команды
- yarn run lint:style — проверить SCSS-файлы. Для VSCode необходимо установить плагин . Для WebStorm или PHPStorm необходимо включить Stylelint в Languages & Frameworks - Style Sheets - Stylelint (ошибки будут исправлены автоматически при сохранении файла)
- yarn run lint:style --fix — исправить ошибки в SCSS-файлах
- yarn run dev — запуск сервера для разработки проекта
- yarn run build — собрать проект с оптимизацией без запуска сервера
- yarn run build views — скомпилировать Pug-файлы
- yarn run build styles — скомпилировать SCSS-файлы
- yarn run build scripts — собрать JS-файлы
- yarn run build images — собрать изображения
- yarn run build webp — сконвертировать изображения в формат.webp
- yarn run build sprites — собрать спрайты
- yarn run build fonts — собрать шрифты
- yarn run build favicons — собрать фавиконки
- yarn run build gzip — собрать конфигурацию Apache
Компонентный подход к разработке сайтов
- аждый БЭМ-блок имеет свою папку внутри src/blocks/modules
- папка одного БЭМ-блока содержит в себе один HTML-файл, один SCSS-файл и один JS-файл (если у блока используется скрипт)
- HTML-файл блока импортируется в файл src/views/index.html (или в необходимый файл страницы, откуда будет вызываться блок)
- SCSS-файл блока импортируется в файл src/blocks/modules/_modules.scss
- JS-файл блока импортируется в src/js/import/modules.js
Пример структуры папки с БЭМ-блоком:
Blocks ├── modules │ ├──header │ │ ├── header.html │ │ ├── header.js │ │ ├── header.scss
Чтобы вручную не создавать соответствующие папку и файлы, достаточно в консоли прописать команду bem create my-block — для создания папки БЭМ-блока, где my-block — имя БЭМ-блока
Страницы проекта
- страницы проекта находятся в папке src/views/pages
- главная страница: src/views/index.html
Шрифты
- шрифты находятся в папке src/fonts
- используйте форматы .woff и.woff2
- шрифты подключаются в файл src/styles/base/_fonts.scss
- сконвертировать локальные шрифты можно с помощью данного сервиса
Изображения
- изображения находятся в папке src/img
- изображение для генерации фавиконок должно находиться в папке src/img/favicon и иметь размер не менее 1024px x 1024px
- изображения автоматически конвертируются в формат.webp . Подробная информация по использованию .
Сторонние библиотеки
- все сторонние библиотеки устанавливаются в папку node_modules
- для их загрузки воспользуйтеcь командой yarn add package_name
- для подключения JS-файлов библиотек импортируйте их в самом начале JS-файла БЭМ-блока (то есть тот БЭМ-блок, который использует скрипт), например:
Import $ from " jquery" ;
- для подключения стилевых файлов библиотек импортируйте их в файл src/styles/vendor/_libs.scss
- JS-файлы и стилевые файлы библиотек самостоятельно изменять нельзя
⚠️ Если в вашем проекте используется несколько библиотек, которые необходимо подключать на нескольких страницах, во избежании ошибок нужно:
- по пути src/js/import создать папку pages
- в папке pages создать js-файл для страницы, например, pageA.js , и импортировать туда библиотеку, которая будет использоваться только на этой странице
- аналогично проделать шаг для дополнительных страниц
- в файле webpack.config.js в точку входа добавить js-файлы страниц, пример:
Entry: { main: " ./src/js/index.js" , pageA: " ./src/js/import/pages/pageA.js" , pageB: " ./src/js/import/pages/pageB.js" }
- подключить скомпилированные js-файлы на необходимых страницах
CSS-сетка smart-grid
В сборщик включена CSS-сетка smart-grid от Дмитрия Лаврика . Она позволяет избавиться от лишних классов в разметке за счёт использования примесей в SCSS и ускоряет адаптивную вёрстку. Конфигурация уже настроена в соответствии с сеткой Bootstrap . Пример использования:
Items { @include row-flex (); @include md (justify-content , center ); .item { @include col (); @include size (3 ); @include size-md (5 ); @include size-xs (10 ); } }
В наше время сборка front-end’a является уже неким стандартом в разработке сайтов и веб-приложений. Есть множество вариантов сборки ресурсов сайта и каждый разработчик выбирает для себя сам, какие инструменты для этого использовать. Я же хочу поведать о процессе сборке с помощью менеджера задач, которому отдал предпочтение, а именно GULP.
В двух слова о Gulp
Gulp — это менеджер задач (как grunt) для сборки ресурсов и выполнения рутинных задач. Он прост в использовании. Это крайне полезный швейцарский нож и может выполнять множество функций. А самое главное он быстр.
Установка Gulp
Прежде всего для работы gulp необходимо установить node.js
Находясь в корне проекта инициализируем проект для npm.
Npm init
Установим gulp и добавим его в зависимости проекта
Npm install --save-dev gulp
Теперь необходимо создать файл в корне проекта gulpfile.js и создадим пустую задачу для примера.
Var gulp = require("gulp"); gulp.task("default", function() { // место для кода, который будет выполняться в задаче });
Собственно так и будут выглядеть задачи в gulp. Практически для каждой задачи, которую может понадобиться выполнить для сборки вашего front-end’a есть плагины. Об установке и использовании плагинов далее.
Сборка css
Думаю в наши дни уже все используют css препроцессоры. Если вы еще не используете какой-либо css препроцессор, вам срочно необходимо ознакомиться с или и начать использовать sass или less в работе.
Я предпочитаю использовать sass, поэтому покажу на примере плагина sass.
Установка плагина
Npm install gulp-sass --save-dev
Две простейших задачи для сборки и live сборки sass файлов при их изменении.
Var gulp = require("gulp"); var sass = require("gulp-sass"); //Задача для сборки gulp.task("sass", function () { return gulp.src("./sass/**/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(gulp.dest("./css")); }); //Задача, после запуска которой, gulp будет следить за изменениями файлов gulp.task("watch", function () { gulp.watch("./sass/**/*.scss", ["sass"]); });
Теперь прописав в консоли gulp sass , gulp соберет все ваши sass файлы в указанной директории.
А выполнив gulp watch запустится процесс, который будет следить за изменением sass файлов, и при изменении сразу их компилировать.
Для остановки процесса достаточно нажать на клавиатуре Ctrl + C.
Объединение JavaScript файлов
Для этого необходимо установить плагин gulp-concat .
Npm install --save-dev gulp-concat
Теперь добавим задачу в наш gulpfile.js .
Var gulp = require("gulp"); var sass = require("gulp-sass"); var concat = require("gulp-concat"); gulp.task("sass", function () { return gulp.src("./sass/**/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(gulp.dest("dist/css")); }); gulp.task("scripts", function() { return gulp.src("src/**/*.js") .pipe(concat("app.js")) .pipe(gulp.dest("dist/js")); }); gulp.task("watch", ["sass", "scripts"], function() { gulp.watch("css/**/*.sass", ["sass"]); gulp.watch("src/**/*.js", ["scripts"]); });
Обратите внимание, что задачи sass и scripts находятся в зависимостях у задачи watch.
Теперь скрипты будут объединяться по команде gulp scripts или же при gulp watch .
Минификация ресурсов
Для минификации JavaScript файлов необходимо установить плагин uglify , а для минификации sass файлов необходимо добавить опцию {outputStyle: "compressed"} .
Var gulp = require("gulp"); var sass = require("gulp-sass"); var concat = require("gulp-concat"); var uglify = require("gulp-uglify"); gulp.task("sass", function () { return gulp.src("./sass/**/*.scss") .pipe(sass({outputStyle: "compressed"}).on("error", sass.logError)) .pipe(gulp.dest("dist/css")); }); gulp.task("scripts", function() { return gulp.src("src/**/*.js") .pipe(concat("app.js")) .pipe(uglify()) .pipe(gulp.dest("dist/js")); }); gulp.task("watch", ["sass", "scripts"], function() { gulp.watch("css/**/*.sass", ["sass"]); gulp.watch("src/**/*.js", ["scripts"]); });
Версионность ресурсов
Версионность ресурсов необходима для контроля кешированных файлов в браузерах посетителей. Если вы работаете с html файлами, то имена создаваемым файлам и пути к ним можно генерировать с помощью плагина gulp-rev или gulp-rev-all . С помощью них выходят файлы файлы с именами такого типа: app-ef62e7.js .
Но когда вы работаете с движками сайтов и вам нужно в определенном файле (любого формата), в определенном месте прописать необходимые пути, то реализовать это можно таким способом.
В первую очередь установим плагины gulp-html-replace и gulp-rename .
Npm install gulp-html-replace --save-dev npm install gulp-rename --save-dev
gulp-rename — будет называть файлы, а gulp-html-replace будет прописывать пути в указанных файлах.
Имена файлов
Теперь дополним наш gulpfile новыми строками:
Var gulp = require("gulp"); var sass = require("gulp-sass"); var concat = require("gulp-concat"); var uglify = require("gulp-uglify"); var htmlreplace = require("gulp-html-replace"); var rename = require("gulp-rename"); var time = Date.now(); gulp.task("path", function () { gulp.src("/assets_tpl.php") .pipe(htmlreplace({ "js": [ "/dist/js/app-"+time+".js" ], "css": [ "/dist/css/bundle-"+time+".css" ] })) .pipe(rename("assets.php")) .pipe(gulp.dest("/")); }); gulp.task("sass",["path"], function () { return gulp.src("./sass/**/*.scss") .pipe(sass({outputStyle: "compressed"}).on("error", sass.logError)) .pipe(concat("bundle-"+time+".css")) .pipe(gulp.dest("dist/css")); }); gulp.task("scripts",["path"], function() { return gulp.src("src/**/*.js") .pipe(concat("app-"+time+".js")) .pipe(uglify()) .pipe(gulp.dest("dist/js")); }); gulp.task("watch", ["sass", "scripts"], function() { gulp.watch("css/**/*.sass", ["sass"]); gulp.watch("src/**/*.js", ["scripts"]); });
Переменная time создана для генерации уникального числа в момент инициализации gulp скрипта. В данном случае это простой timestamp. Он и будет прописываться в имя файла.
Пути к файлам
Также создана новая задача path , которая по файлу-шаблону assets_tpl.php сгенерирует файл assets.php с боевыми путями к файлам.
Вот как выглядит файл-шаблон assets_tpl.php:
Вместо указанных тегов в сгенерированном файле будут прописаны пути к файлу.
Так же стоит обратить внимание, что задача path добавлена как зависимость для задач sass и scripts .
Но теперь у нас при каждом выполнении любой из задач будут создаваться новые файлы, а старые будут оставаться. Для того, что бы хранились только свежие файлы, необходимо чистить папку с скомпилированными ресурсами. Для этого необходимо установить плагин gulp-clean .
Npm install --save-dev gulp-clean
Затем добавим задачу с флагом {read: false} , для ускоренного удаления.
Var clean = require("gulp-clean"); gulp.task("clean", function () { return gulp.src("dist", {read: false}) .pipe(clean()); });
И создадим задачу build , которая будет выполнять чистку, а затем собирать все наши ресурсы.
Gulp.task("build",["clean","sass","scripts","path"], function () {});
Таким образом выполнив в консоли команду gulp build , gulp полностью автоматически соберет весь front-end для проекта. Данную команду также можно повесить на хук после деплоя проекта на сервер / хостинг.
Заключение
В данной статье показан пример для ознакомления, как можно собирать файлы стилей и скриптов для ваших проектов. Вариантов и плагинов множество и выбрать каждый может под себя сам.
Если вам полезен данный материал, подписывайтесь на мой блог, чтобы получать уведомления о новых публикациях на темы: верстки, javascript и разработки кроссплатформенных мобильных приложений.
Таск-раннеры и системы сборки сильно ускоряют работу, автоматизируя компиляцию, тестирование и другие рутинные задачи. Как и в любой другой области, на этом рынке существует сильная конкуренция. До 2014 года среди них главенствовал таск-раннер grunt, но позже из состава проекта выделилась небольшая команда, которая решила делать альтернативный инструмент, gulp, ориентированный на сборку проекта.
Чтобы помочь вам определиться с выбором, в рамках статьи рассмотрим основные таск-менеджеры:
- grunt
а также коснемся других средств и способов сборки.
Забегая немного вперед, скажем, что мы в WaveAccess пользуемся именно gulp. Внедрить инструмент оказалось очень легко: у семейства продуктов JetBrains (IDEA, WebStorm, ReSharper), которые мы используем уже много лет, есть отличные плагины для работы с gulp/grunt и npm/nodejs.
Таск-менеджер vs. система сборки проекта: в чем разница?
Таск-менеджер - инструмент для автоматизации задач. В конфигурации раннеров можно записать имена этих задач; функцию, которая их выполняет; плагины для ускорения стандартных действий, но сами задачи могут быть произвольными. Например:
- Задачи для деплоя (zip проекта, загрузка проекта на удаленный сервер и тп)
- Задачи по сборке проекта (минификация, оптимизация, проверка кода на валидность и тп)
- Задачи для миграции данных и т.д.
Примеры таких инструментов - grunt и gulp.
Система сборки - это инструмент, который решает только одну типовую задачу сборки проекта на java script, в которую входят:
- конкатенация,
- проверка кода на валидность,
- минификация кода, и тд.
К подобным инструментам относятся Webpack, Broccoli, Brunch, Browserify и другие.
Все подобные frontend-задачи можно автоматически выполнять при помощи других средств: к примеру, с помощью npm run, о котором мы также поговорим в статье.
Пример
Рассмотрим gulp-файл для сборки проекта:
Const gulp = require (‘gulp’); const coffee = require (‘gulp-coffee’); const concat = require (‘gulp-concat’); const uglify = require (‘gulp-uglify’); const imagemin = require (‘gulp-imagemin’); const sourcemaps = require (‘gulp-sourcemaps’); const del = require (‘del’); }
Но сборка - это частный случай большой типовой задачи. Для gulp можно написать и другой config - скажем, для деплоя:
Var gulp = require("gulp"); var zip = require("gulp-zip"); var del = require("del"); var install = require("gulp-install"); var runSequence = require("run-sequence"); var awsLambda = require("node-aws-lambda"); gulp.task("clean", function(cb) { del(["./dist", "./dist.zip"], cb); }); gulp.task("copy", function() { return gulp.src("index.js") .pipe(gulp.dest("dist/")); }); gulp.task("node-mods", function() { return gulp.src("./package.json") .pipe(gulp.dest("dist/")) .pipe(install({production: true})); }); // Clean up all aws-sdk directories from node_modules. We don"t // need to upload them since the Lambda instance will already // have it available globally. gulp.task("clean-aws-sdk", function(callback) { del(["dist/node_modules/**/aws-sdk"], callback); }); gulp.task("zip", function() { return gulp.src(["dist/**/*", "!dist/package.json"]) .pipe(zip("dist.zip")) .pipe(gulp.dest("./")); }); gulp.task("upload", function(callback) { awsLambda.deploy("./dist.zip", require("./lambda-config.js"), callback); }); gulp.task("deploy", function(callback) { return runSequence(["clean"], ["copy"], ["node-mods"], ["clean-aws-sdk"], ["zip"], ["upload"], callback); });
A можно описывать новые задачи как комбинации уже существующих:
Gulp.task(‘deploy’, gulp.series (‘clean’, ‘copy’, ‘node-mods’, ‘clean-aws-sdk’, ‘zip’, ‘upload’));
В этом и заключается отличие. Теперь рассмотрим основные инструменты.
gulp vs. grunt
Итак, перед нами два таск-раннера: gulp и grunt. Оба используют node.js и npm, а задачи им ставят, используя javascript.
На первый взгляд они схожи, однако у gulp есть то, что делает его более удобным именно для сборки: умение параллельно обрабатывать задачи и компактный конфиг, лаконичное API. Давайте посмотрим поближе их принцип работы.
Потоковая передача данных
Перед нами грант-файл, который осуществляет сборку и обработку CSS.
Из него видно, что grunt при запуске каждого процесса:
открывает файл;
запускает процесс;
сохраняет изменения;
закрывает обработанный файл, чтобы предотвратить вмешательство в него следующего процесса;
записывает файл в итоговую папку.
То есть, цепочка включает в себя создание нескольких временных папок и сохранение промежуточных файлов:
Плагины пишут разные авторы. Чтобы каждый плагин мог работать с файлами, обходя сохранение, файлы нужно представить в виде объектов. В gulp эту задачу выполняет виртуальная файловая система Vynyl-FS. И gulp сразу передает файл следующему процессу без создания временных файлов и без сохранения на диск.
Та же самая конфигурация для gulp уже компактнее:
Его общий механизм работы - потоковая обработка файлов без записи на диск:
Последовательность выполнения задач
Есть и другое отличие: gulp по умолчанию асинхронно выполняет таски. В этом есть и плюсы, и минусы. В том же конфигурационном файле мы даем команду считать файлы из директории dev/*scss и отправить их в SASS.
Потоки отправляют результат в.pipe. Метод.pipe позволяет собирать результат в буфер по частям, а когда он заполнен, сразу отправлять информацию в поток для чтения, еще не закончив получать содержимое директории.
Последовательное выполнение задач делает gulp быстрым и мощным, но изредка возникает необходимость все же выполнить задачи синхронно, как в grunt. Проблему можно решить через обратный вызов, возвращение потока или Promise . Более подробно задача разобрана на Хабре . Есть и альтернативный вариант на самом сайте npm.js
Если вы пользуетесь grunt, но вас привлекает потоковая передача данных -- тот же модуль Vynyl-FS можно использовать для ее реализации в grunt.
Лаконичное API gulp имеет всего 5 методов:
Task(name, fn). Регистрирует функцию с именем. Можно указать зависимость от других тасков, если нужно их выполнить сначала.
Run(tasks...). Выполняет задачи.
Watch(glob, fn). Выполняет функцию, если файл на месте glob меняется.
Src(glob). В качестве параметра принимает маску файлов и возвращает поток, представляющий эти файлы. Затем поток может быть передан на вход плагинам.
Dest(folder). Сохраняет файлы в указанную папку.
Особенно хотелось бы отметить наличие.watch() в “родном” API проекта, ведь слежение за постоянными изменениями файлов является важнейшей составляющей сборки. Краткость API дает возможность этому таск-менеджеру сфокусироваться на своей основной задаче – сборке проектов.
Альтернативы gulp и grunt
Несмотря на популярность gulp (больше 130 к скачиваний в день) и grunt (более 86 к скачиваний в день согласно npmjs.com), разработчики видят в этих системах и свои недостатки: к примеру, зависимость от плагинов, неполная документация, неудобный дебаггинг. В качестве альтернативы можно рассмотреть системы сборки проектов (такие как Broccoli и Webpack) или npm-скрипты.
Системы сборки проектов
Рассмотрим несколько альтернативных решений на платформе Node.js. Для сборки проекта они могут заменить gulp и grunt.
Эта система, как и gulp, возникла как конкурент таск-раннеру grunt, однако разработчики изначально задумывали ее именно как помощник для сборки со всеми преимуществами и недостатками. Он без настроек “поймет”, что *.js - это файл со скриптами, *.coffee - это CoffeeScript; его конфиг более компактен. Однако никаких произвольных действий на этапе сборки он совершить не сможет.
Вот конфиг-файл Brunch. Он написан на CoffeeScript (можно также писать на JS):
Exports.config = files: javascripts: joinTo: "javascripts/app.js": /^app/ "javascripts/vendor.js": /^(bower_components|vendor)/ stylesheets: joinTo: "stylesheets/app.css" order: after: ["vendor/styles/helpers.css"] templates: joinTo: "javascripts/app.js"
Здесь хочется обратить внимание на операторы joinTo и order. На уровне конфига Brunch понимает, что придется собирать файлы в нужном порядке. В результате, конфиг занимает 20-30 строк.
Broccoli
Инди-инструмент, который находится на стадии разработки. Его разработчики хотели создать конкуренцию уже gulp.
По сравнению с gulp, инструмент Broccoli использует другие принципы:
Ускорение сборки. Каждый плагин реализует промежуточное кэширование результатов сборки вместо частичной пересборки только нужных файлов.
Деревья вместо файлов. Gulp лучше всего трансформирует один файл в один итоговый. Broccolli по умолчанию использует только деревья, а не файлы, и их трансформирует в другие деревья (вырожденные из одной вершины).
В данный момент инструмент активно развивается, появляются новые плагины, но для серьезных проектов его использовать рано: плагинов пока недостаточно.
Webpack - гибкая модульная система сборки. Она обладает непривычным синтаксисом, но сама воспринимает любые синтаксисы модулей.
Понимая, что придется конкурировать с такими гигантами как gulp, создатели решили облегчить нам жизнь при разработке больших проектов. И добавили в утилиту:
Умение автоматически строить дерево зависимостей и ресурсов.
Удобные средства для реализации динамической подгрузки.
Совместимость с практически любыми модулями (AMD, UMD, ES 2015, Common JS, сторонние модули на их основе).
Совместимость с препроцессорами (SASS, Babel, Coffee Script, Type Script и т.д.).
Live Reload (технологию асинхронной загрузки, при которой браузер обновляет не страницы целиком, а отдельные приложения).
Возможность делить код и генерировать множество bundle-файлов, избегая создания одного тяжелого bundle.js.
Умение оптимизировать код.
Отдельно можно отметить гибкий подход к зависимостям. Модулем может стать JS, CSS и HTML-файл, и даже JPEG с PNG. Можно использовать require(“myJSfile.js”) и require(“myCSSfile.css”), делить и использовать части артефакта повторно.
Подробнее о возможностях, конфигурации инструмента, плагинах можно найти на Github, в презентации с Fronttalks: глубокое погружение в Webpack .
npm скрипты
Задачи по сборке можно решить и при помощи npm-скриптов. Многих отпугивает эта идея: мало возможностей, скрипты недостаточно быстрые в сравнении с gulp или Webpack. Тем не менее, эти недостатки преувеличены.
Возможности npm-скриптов
Npm-скрипты решают довольно много задач. Так, например, можно реализовать скрипты ловушек:
{ "name": "npm-scripts-example", "version": "1.0.0", "description": "npm scripts example", "scripts": { "prebuild": "echo I run before the build script", "build": "cross-env NODE_ENV=production webpack" "postbuild": "echo I run after the build script" } }
Сценарии будут загружаться по порядку согласно префиксам: prebuild, например, стартует перед build, потому что у него есть префикс pre. Соответственно, postbuild будет загружен последним. Команда npm run build запустит их в нужном порядке.
Можно вызывать один скрипт из другого, чтобы декомпозировать сложные задачи. Например, здесь задача prebuild вызывает задачу clean.
{ "name": "npm-scripts-example", "version": "1.0.0", "description": "npm scripts example", "scripts": { "clean": "rimraf ./dist && mkdir dist", "prebuild": "npm run clean", "build": "cross-env NODE_ENV=production webpack" } }
Если задача становится слишком сложной, всегда можно вызвать отдельный файл:
{ "name": "npm-scripts-example", "version": "1.0.0", "description": "npm scripts example", "scripts": { "build": "node build.js" } }
За счет стриминга gulp для задач сборки стал гораздо удобнее, чем grunt. Но его можно реализовать и через npm. В Windows и Unix стриминг делается по умолчанию, без сохранения промежуточных файлов.
К примеру, в Unix можно сделать grep содержимого файла и направить его в новый файл:
Grep ‘My Name’ bigFile.txt > linesThatHaveMyName.txt
Редирект(>)направляет нужные строки в конечный файл. Задача выполняется без сохранения промежуточных файлов.
Но есть и неудобства: нельзя оставлять комментарии в package.json. Выходом может стать создание коротких скриптов с понятными названиями, нацеленных на какую-то одну небольшую задачу. Более подробно вопрос замены таск-раннеров npm-скриптами хорошо освещен в англоязычной статье Why I Left Gulp and Grunt for npm Scripts.
Итог
На рынке существует большая конкуренция инструментов для автоматизации рутинных задач (например, gulp и grunt), а также инструментов для автоматизации сборки проекта (Webpack, Broccoli, Medusa, Browserify и т.д.).
Если смотреть на таск-раннеры, то gulp по сравнению с grunt более прост, понятен и производителен: выигрывает за счет экономии на дисковых операциях. Но у grunt больше плагинов (например, есть плагин для тестирования). Из-за этого у него остается много поклонников.
Если же говорить только о сборке, то у gulp есть все преимущества перед grunt:
Поточная архитектура для передачи файлов по цепочке, которую обеспечивает модуль Vynyl-FS.
По умолчанию - асинхронное выполнение задач.
Лаконичное API всего из 5 функций.
В то же время, для сборки Webpack является не менее интересным инструментом. В нем предусмотрена технология Live Reload, ускоряющая обновление браузера. Это огромный плюс: технология экономит время на нажатие кнопки обновления, которую разработчикам приходится нажимать постоянно. В gulp также есть Live Reload, но Webpack сложно сравнивать с gulp или grunt, так как он “заточен” только под билд и не “умеет” решать произвольные задачи.
Все эти решения прекрасно интегрируются с семейством продуктов от JetBrains, однако мы в WaveAccess предпочли именно grunt за широкие возможности и для верстальщиков, и для frontend-специалистов.
Если у Вас возникли вопросы и вам необходима разработка web-проекта, пишите нам на [email protected]
- Frontend
- Grunt
- Gulp
- Task runners
Приветствую. Если вы занимаетесь frontend разработкой, то могли заметить, что часто приходится выполнять одни и те же задачи. Было бы здорово все это автоматизировать и свести объем рутины к минимуму. В этом вам могут помочь таск-менеджеры и сборщики проектов, такие как Gulp и Grunt.
Gulp – это ответвление от проекта Grunt. От своего родителя он взял лучшие практики. Код инструкций пишется на JavaScript. Работает быстрее, чем Grunt в несколько раз.
Gulp предоставляет по-настоящему широкие возможности. Он облегчит и ускорит frontend разработку. Перечислю основные задачи, которые сборщик проектов вам поможет решить.
- Создание веб-сервера для отладки
- Автоматическая перезагрузка страниц при внесении изменений (LiveReload)
- Слежение за изменениями в файлах проекта
- Использование препроцессоров HTML, CSS, JS
- Объединение файлов и их минификация
- Автоматическое создание вендорных префиксов для браузеров (Autoprefixer)
- Автоматизация управления файлами и директориями
- Запуск и контроль внешних команд операционной системы
- Запуск и контроль приложений
- Оптимизация изображений (сжатие, изменение размеров и т.д.)
- Выгрузка проекта на внешний сервер с помощью FTP, SFTP, Git и т.д.
- Подключение и использование дополнительных плагинов (сегодня их уже 3570 штук; решение можно
- найти практически для всех повседневных рутинных задач и не только)
- Автоматизация ручного труда
Установка
Для работы Gulp требуется Node.js. Скачать его можно на официальном сайте . Советую ставить LTS версию программной платформы. После инсталляции Node.js можно переходить к установке Gulp. Для этого откройте консоль операционной системы и выполните следующую команду:
Мы используем параметр -g , который позволяет установить Gulp глобально в операционной системе, без привязки к конкретному проекту.
Важно, чтобы в пути к директории установки не было русских символов. Из-за этого некоторые плагины gulp могут работать некорректно.
Окей, пришло время создать проект, в котором мы будем использовать Gulp. Перейдите в директорию проекта и выполните команду:
Это запустит скрипт, который задаст вам несколько вопросов о проекте. В результате будет сконфигурирован файл для npm package.json . Это манифест проекта. Он содержит список пакетов, которые используются в проекте и другую информацию. На этом этапе я внес следующие данные, адаптируйте под свой проект.
name: (bybtc-landing) version: (1.0.0) description: Landing Page for byBTC entry point: (index.js) test command: git repository: https://github.com/Neuropassenger/bybtc-landing.git keywords: landing
Если какой-то вопрос хотите пропустить, то просто нажимайте Enter. Будет использовано значение по умолчанию. Теперь можно установить Gulp для нашего проекта. Выполните команду:
npm i --save-dev gulp
Gulp будет установлен в директорию проекта, а параметр –save-dev добавит его в зависимости package.json. Это позволит другому разработчику, открывшему ваш проект, быстро развернуть его на своей машине (с помощью команды npm i ).
Если все прошло хорошо, то в папке проекта должен появиться каталог node_modules . Он содержит установленный пакет gulp и все зависимости, необходимые для его работы.
Настало время создать базовую структуру проекта. Я придерживаюсь тех же названий каталогов, что и многие разработчики. Советую это делать и вам, чтобы другой человек мог быстро разобраться в структуре вашего проекта. Впрочем, никто не запрещает вам использовать такие названия, какие вам хочется.
Создаем две папки в корне проекта:
- /src/ – исходный код проекта во время разработки, здесь вы будете редактировать файлы
- /dist/ – файлы и папки проекта после сборки, готовый продукт
Каталог /dist/ будет заполняться автоматически при сборке проекта. Займемся пока что /src/ . Создайте внутри следующие папки:
- /css/ – для каскадных таблиц стилей
- /js/ – для JavaScript-сценариев
- /img/ – для изображений
- /fonts/ – для шрифтов
- /sass/ – для файлов препроцессора SASS (если используете SASS)
- /libs/ – для сторонних библиотек
Если все готово, то пора перейти к созданию файла gulpfile.js в корне проекта, который поможет настроить Gulp. Именно здесь вы можете создать инструкции Gulp, которые помогут автоматизировать часть рутинных задач.
Инструкции Gulp
Любая инструкция создается в gulpfile.js с помощью функции gulp.task() . Первый параметр – это название инструкции. Затем идет массив из названий инструкций, которые нужно выполнить до запуска определяемой инструкции. Последний параметр – это функция, тело которой определяет то, что делает данная инструкция.
gulp.task("название_инструкции", ["инструкция_выполняющаяся_перед_текущей", "еще_одна_инструкция"], function() { // Какие-то действия });
Для вызова инструкции необходимо использовать в консоли следующую команду:
gulp название_команды
Компиляция SASS в CSS
Начнем с компиляции SASS в CSS. Установим пакет gulp-sass:
npm i --save-dev gulp-sass
Сначала необходимо подключить используемые пакеты в gulpfile.js . Сделаем это:
var gulp = require("gulp"), sass = require("gulp-sass");
Теперь создадим инструкцию, которая будет выполнять компиляцию SASS в CSS:
gulp.task("sass", function() { return gulp.src("src/sass/**/*.sass") .pipe(sass()) .pipe(gulp.dest("src/css")); });
В первой строке инструкции указываем исходные файлы для компиляции. В конкретном примере будут взяты все файлы с расширением .sass , находящиеся внутри папки /src/sass/ и ее подпапках. Можно выбирать и конкретные файлы. Вот примерный список того, как вы можете задавать пути к исходным файлам.
- src/sass/main.sass – выбор файла main.sass
- src/sass/*.sass – выбор всех файлов с расширением sass
- src/sass/**/*.sass – выбор всех файлов с расширением sass во всех вложенных папках в папке sass
- !src/sass/main.sass – исключение файла main.sass
- [‘!src/sass/main.sass’, ‘src/sass/second.sass’] – исключение массива файлов main.sass и second.sass
- src/sass/**/*.+(scss|sass) – выбрать все файлы scss и sass во всех вложенных папках в sass
Теперь создаем в папке /src/sass/ файл main.sass и определим в нем немного стилей:
body color: red font-size: 20px
Сохраняем файл. Теперь мы можем проверить, как работает компиляция. В консоли выполняем команду:
Проверяем каталог /src/css/ . В нем должен находится только что скомпилированный CSS файл. Видите? Отлично! Двигаемся дальше.
Автообновление страниц (LiveReload)
Перейдем к автоматизации обновления страниц при их изменении, т.е. настроим LiveReload . Это одна из самых популярных задач, стоящих перед . Нам понадобится пакет npm Browsersync для автоматического обновления браузера. Установим его:
npm i --save-dev browser-sync
Подключим browser-sync пакет в начале gulpfile.js , как мы это делали ранее с пакетами gulp и gulp-sass :
browserSync = require("browser-sync");
Создадим инструкцию для запуска Browsersync:
gulp.task("browser-sync", function() { browserSync({ server: { baseDir: "src" } }); });
Все, что мы сделали – это вызвали запуск Browsersync и указали директорию проекта с исходными файлами. Есть и другие настройки для Browsersync . Можете узнать о них в документации.
Добавим еще один pipe к инструкции sass , с помощью которого будет происходить обновление стилей при компиляции CSS файлов. Указываем параметр stream: true . Это позволит обновлять стили потоково , без полной перезагрузки страницы.
Pipe(browserSync.reload({ stream: true; }))
Затем создадим инструкцию, которая будет следить за изменениями в файлах и перезагружать страницу при необходимости.
gulp.task("watch", ["browser-sync"], function() { gulp.watch("src/sass/**/*.sass", ["sass"]); gulp.watch("src/js/**/*.js", browserSync.reload); gulp.watch("src/**/*.html", browserSync.reload); });
Поясню. Перед запуском выполняется инструкция browser-sync , т.е. стартует веб-сервер для отладки проекта. После этого выполняется сама инструкция watch . Для слежения за изменениями в файлах используем gulp.watch() .
Внутри анонимной функции мы выполняем 3 раза gulp.watch с двумя параметрами. Первый параметр – файлы, за которыми нужно следить, второй – действия, которые нужно выполнить при изменении файлов, т.е. выполнить инструкцию sass или обновить страницу.
Обратите внимание на первый gulp.watch . Вместо browserSync.reload мы передаем в массиве инструкцию sass , которую нужно выполнить, если файлы были изменены. В ней, как вы помните, мы потоково обновляем стили на странице.
Минификация и объединение файлов
Почти в любом проекте приходится использовать сторонние библиотеки. Их количество может составлять от 5 и до бесконечности. Соответственно, все они должны быть включены в готовый продукт. Все это дело было бы неплохо оптимизировать, а именно:
- минифицировать (сжать) используемые библиотеки
- уменьшить количество запросов к серверу, объединив библиотеки в единый файл
Добавим в исходные файлы проекта несколько библиотек. Для этого я использую Bower , пакет для NPM . Установим Bower:
Создаем файл конфигурации .bowerrc в корне проекта для Bower, где укажем ему, куда сохранять библиотеки:
{ "directory": "src/libs/" }
Установим, для примера, библиотеку jQuery и слайдер slick :
bower i jquery slick-carousel
Теперь можем заняться конкатенацией и сжатием библиотек. Для этого будем использовать пакеты gulp-concat и gulp-uglifyjs касательно JavaScript-файлов. Установим их:
npm i --save-dev gulp-concat gulp-uglifyjs
Касательно CSS – пакет gulp-cssnano . Устанавливаем:
npm i --save-dev gulp-cssnano
Минифицированные файлы обычно имеют суффикс .min . Добавить его нам поможет пакет gulp-rename . Устанавливаем:
npm i --save-dev gulp-rename
Начнем с подключения установленных плагинов в gulpfile.js :
concat = require("gulp-concat"), uglifyJs = require("gulp-uglifyjs"), cssNano = require("gulp-cssnano"), rename = require("gulp-rename");
JavaScript
Создадим инструкцию, которая позволит нам сжимать и объединять JavaScript-файлы:
gulp.task("min-js", function() { return gulp.src([ "src/libs/jquery/dist/jquery.min.js", "src/libs/slick-carousel/dist/slick.min.js" ]) .pipe(concat("libs.min.js")) .pipe(uglifyJs()) .pipe(gulp.dest("src/js")); });
Внутри анонимной функции инструкции min-js мы сначала указываем пути на JavaScript-файлы библиотек в виде массива. Затем с помощью concat объединяем библиотеки в единый файл libs.min.js uglifyJs . И, наконец, сохраняем результат в папку /src/js/ .
Инструкцию можно проверить с помощью команды в консоли:
В папке /src/js/ появится файл libs.min.js , в котором объединены и сжаты используемые в проекте JavaScript-файлы библиотек.
CSS
Создадим в каталоге /src/css/ файл libs.sass . Будем в него импортировать CSS-файлы библиотек. Для примера с помощью Bower я скачал библиотеку Bootstrap :
bower i bootstrap
Откроем файл libs.sass и импортируем в него CSS-файл Bootstrap:
@import "src/libs/bootstrap/dist/css/bootstrap"
Таким образом, мы соберем все CSS-файлы библиотек в одном месте, а именно в файле libs.sass с помощью импорта. Теперь создадим инструкцию для сжатия:
gulp.task("min-css", ["sass"] , function() { return gulp.src("src/css/libs.css") .pipe(cssNano()) .pipe(rename({ suffix: ".min" })) .pipe(gulp.dest("src/css")); });
Перед сжатием мы компилируем CSS из SASS с помощью инструкции sass , которую мы указали в массиве после имени инструкции min-css .
В первой строке мы берем конкретный файл, libs.css . Далее сжимаем его с помощью cssNano . Затем с помощью rename добавляем суффикс .min . Результат сохраняем в папке /src/css/ .
Проверяем инструкцию:
Если вы все сделали правильно, то в папке /src/css/ должно появиться два файла. libs.css и libs.min.css . Сравните их размеры.
Автоматическое добавление вендорных префиксов (Autoprefixer)
При использовании свежих возможностей CSS необходимо расставлять вендорные префиксы для правильной работы стилей. Делать такие вещи вручную – неблагодарное занятие. Поэтому заставим Gulp это сделать за нас.
Для начала установим gulp-autoprefixer :
npm i --save-dev gulp-autoprefixer
Подключим установленный пакет в gulpfile.js :
autoprefixer = require("gulp-autoprefixer");
Окей, теперь мы можем использовать autoprefixer в инструкции sass . Сделаем это после вызова .pipe(sass()) , т.к. вендорные префиксы нужно расставить после того, как SASS будет преобразован в CSS. Добавим новый pipe :
Pipe(autoprefixer([ "last 10 versions" ], { cascade: true }))
Первым параметром autoprefixer мы передаем массив, в котором указываем, что хотим включить поддержку последних 10 версий браузеров. Второй параметр – это настройки, где мы указываем, что хотим видеть красивый код на выходе, т.е. включаем каскадность.
Проверяем, добавляя в main.sass новое свойство flex . Запускаем инструкцию sass :
В main.css должны появиться вендорные префиксы. Очень просто, все работает в автоматическом режиме. Замечательно!
Финальная сборка проекта
Последнее, чего хотелось бы коснуться в этом гайде для новичков по Gulp – это финальная сборка проекта. Для этого нам понадобится папка /dist/ , которую мы создали в самом начале. Перед каждой сборкой ее необходимо очищать. Для этого будем использовать пакет NPM del . Установим его:
npm i --save-dev del
Подключим пакет del в gulpfile.js :
del = require("del");
Создадим инструкцию clean для очистки каталога / dist/ перед сборкой:
gulp.task("clean", function() { return del.sync("dist"); });
Теперь можно заняться непосредственно сборкой проекта. Создадим инструкцию build :
gulp.task("build", ["clean", "min-css", "min-js"], function() { var buildCss = gulp.src([ "src/css/libs.min.css", "src/css/main.css" ]) .pipe(gulp.dest("dist/css")); var buildFonts = gulp.src("src/fonts/**/*") .pipe(gulp.dest("dist/fonts")); var buildJs = gulp.src("src/js/**/*") .pipe(gulp.dest("dist/js")); var buildHtml = gulp.src("src/*.html") .pipe(gulp.dest("dist")); });
Перед вызовом инструкции build мы очищаем папку /dist/ на тот случай, если сборка уже проводилась до этого. Затем сжимаем и объединяем JavaScript и CSS файлы с помощью инструкций min-js и min-css соответственно . Попутно компилируем SASS в CSS, т.к. перед выполнением инструкции min-css выполняется инструкция sass .
Внутри тела инструкции мы копируем подготовленные файлы проекта в каталог с готовым продуктом /dist/ . Проверяем работу инструкции:
Все отлично работает! В папке /dist/ теперь находится готовый продукт после сборки, который можно выгружать на рабочий сервер.
Заключение
На этом закончу гайд для новичков по сборке проектов в Gulp. Как видите, все довольно просто. Со временем опубликую еще несколько постов, касающихся Gulp и его плагинов, как только сам хорошенько в них разберусь. А пока пользуйтесь и автоматизируйте свои рутинные задачи во frontend разработке согласно приведенной инструкции. Если появятся вопросы – задавайте в комментариях к посту.
{ "name": "bybtc-landing", "version": "1.0.0", "description": "Landing Page for byBTC", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/Neuropassenger/bybtc-landing.git" }, "keywords": [ "landing" ], "author": "Oleg Sokolov", "license": "ISC", "bugs": { "url": "https://github.com/Neuropassenger/bybtc-landing/issues" }, "homepage": "https://github.com/Neuropassenger/bybtc-landing#readme", "devDependencies": { "browser-sync": "^2.18.13", "del": "^3.0.0", "gulp": "^3.9.1", "gulp-autoprefixer": "^4.0.0", "gulp-concat": "^2.6.1", "gulp-cssnano": "^2.1.2", "gulp-rename": "^1.2.2", "gulp-sass": "^3.1.0", "gulp-uglifyjs": "^0.6.2" } }
var gulp = require("gulp"), sass = require("gulp-sass"), browserSync = require("browser-sync"), concat = require("gulp-concat"), uglifyJs = require("gulp-uglifyjs"), cssNano = require("gulp-cssnano"), rename = require("gulp-rename"), autoprefixer = require("gulp-autoprefixer"), del = require("del"); gulp.task("sass", function() { return gulp.src("src/sass/**/*.sass") .pipe(sass()) .pipe(autoprefixer([ "last 10 versions" ], { cascade: true })) .pipe(gulp.dest("src/css")) .pipe(browserSync.reload({ stream: true })); }); gulp.task("min-css", ["sass"] , function() { return gulp.src("src/css/libs.css") .pipe(cssNano()) .pipe(rename({ suffix: ".min" })) .pipe(gulp.dest("src/css")); }); gulp.task("min-js", function() { return gulp.src([ "src/libs/jquery/dist/jquery.min.js", "src/libs/slick-carousel/dist/slick.min.js" ]) .pipe(concat("libs.min.js")) .pipe(uglifyJs()) .pipe(gulp.dest("src/js")); }); gulp.task("browser-sync", function() { browserSync({ server: { baseDir: "src" } }); }); gulp.task("watch", ["browser-sync"], function() { gulp.watch("src/sass/**/*.sass", ["sass"]); gulp.watch("src/js/**/*.js", browserSync.reload); gulp.watch("src/**/*.html", browserSync.reload); }); gulp.task("clean", function() { return del.sync("dist"); }); gulp.task("build", ["clean", "min-css", "min-js"], function() { var buildCss = gulp.src([ "src/css/libs.min.css", "src/css/main.css" ]) .pipe(gulp.dest("dist/css")); var buildFonts = gulp.src("src/fonts/**/*") .pipe(gulp.dest("dist/fonts")); var buildJs = gulp.src("src/js/**/*") .pipe(gulp.dest("dist/js")); var buildHtml = gulp.src("src/*.html") .pipe(gulp.dest("dist")); });