Gulp с нуля

Use latest JavaScript version in your gulpfile

Most new versions of node support most features that Babel provides, except the / syntax. When only that syntax is desired, rename to , install the module, and skip the Babel portion below.


Node already supports a lot of ES2015+ features, but to avoid compatibility problems we suggest to install Babel and rename your to .

npm install --save-dev @babel/register @babel/core @babel/preset-env

Then create a .babelrc file with the preset configuration.

{"presets""@babel/preset-env"}

And here’s the same sample from above written in ES2015+.

importgulpfrom'gulp';importlessfrom'gulp-less';importbabelfrom'gulp-babel';importconcatfrom'gulp-concat';importuglifyfrom'gulp-uglify';importrenamefrom'gulp-rename';importcleanCSSfrom'gulp-clean-css';importdelfrom'del';constpaths={  styles{    src'src/styles/**/*.less',    dest'assets/styles/'},  scripts{    src'src/scripts/**/*.js',    dest'assets/scripts/'}};exportconstclean=()=>del('assets');exportfunctionstyles(){returngulp.src(paths.styles.src).pipe(less()).pipe(cleanCSS()).pipe(rename({      basename'main',      suffix'.min'})).pipe(gulp.dest(paths.styles.dest));}exportfunctionscripts(){returngulp.src(paths.scripts.src,{ sourcemapstrue}).pipe(babel()).pipe(uglify()).pipe(concat('main.min.js')).pipe(gulp.dest(paths.scripts.dest));}functionwatchFiles(){gulp.watch(paths.scripts.src, scripts);gulp.watch(paths.styles.src, styles);}export{watchFilesaswatch};constbuild=gulp.series(clean,gulp.parallel(styles, scripts));exportdefaultbuild;

Организация файлов

Чтобы не создавать каши из файлов и папок организуем всё правильно с самого начала. Корневая папка носит называние проекта. В моё случае heihei или heiheiru В этой папке мы инициализируем GIT и npm. npm создаст папку node_modules и файлы package.json , package-lock.json. Для GIT мы сами рано или поздно создадим файл gitignore Так как мы будем пользоваться Gulp появится и файл gulpfile.js С ростом числа заданий, которые будет выполнять Gulp нам станет неудобно хранить их все в одном файле. В gulpfile.js мы будем только импортировать другие .js файлы по принципу — на каждое задание один файл. Чтобы хранить эти файлы нам будет нужна папка, назовём её gulp и создадим в корневой. Внутри неё создадим подпапку tasks Всё, что относится непосредственно к сайту положим в папку heiheiru/app index.html положим в корень app а .css файлы, картинки и скрипты мы положим в папки heiheiru/app/assets/styles heiheiru/app/assets/images , heiheiru/app/assets/scripts Так будет выглядить дерево папок в редакоторе Sublime

Sample gulpfile.js

This file will give you a taste of what gulp does.

var gulp =require('gulp');var less =require('gulp-less');var babel =require('gulp-babel');var concat =require('gulp-concat');var uglify =require('gulp-uglify');var rename =require('gulp-rename');var cleanCSS =require('gulp-clean-css');var del =require('del');var paths ={  styles{    src'src/styles/**/*.less',    dest'assets/styles/'},  scripts{    src'src/scripts/**/*.js',    dest'assets/scripts/'}};functionclean(){returndel('assets');}functionstyles(){returngulp.src(paths.styles.src).pipe(less()).pipe(cleanCSS()).pipe(rename({      basename'main',      suffix'.min'})).pipe(gulp.dest(paths.styles.dest));}functionscripts(){returngulp.src(paths.scripts.src,{ sourcemapstrue}).pipe(babel()).pipe(uglify()).pipe(concat('main.min.js')).pipe(gulp.dest(paths.scripts.dest));}functionwatch(){gulp.watch(paths.scripts.src, scripts);gulp.watch(paths.styles.src, styles);}exports.clean= clean;exports.styles= styles;exports.scripts= scripts;exports.watch= watch;var build =gulp.series(clean,gulp.parallel(styles, scripts));gulp.task('build', build);gulp.task('default', build);

Finally: Some notes on Gulp 4 and current gulp 4 build

1. Based on your gulpfile shown, here are some pointers on Gulp 4

Gulp 4 now uses:

  • : Controls the order of what is to be run. It runs the tasks in sequential order until completion, e.g.:
  • ‘gulp.parallel’: Runs the tasks at the same time as gulp 3 previously had done, e.g.:
  • and can be used together to better control the flow of the tasks, e.g.:

2. And quick fix for the current gulp 4 build

In these examples, you will notice that I had used in the task:

This is due to, presumably, a bug in the gulp 4 watch method. I am assuming this is the case, as I could not get the watch task to work by simply using and instead had to use . I said presumably it’s a bug, as when learning gulp 4 myself, others had been using the traditional watch task format with their gulp 4 examples:

I had to come up with this work around in order to use gulp watch. Which is fine, and also could provide other benefits of executing some other tasks after if you wish to.

Разворачивание готовой сборки одной командой

Вы создали сборку, теперь хотите реально создать на ней проект. Достаточно скопировать папку сборки и запустить установку. Вообще при установке должны устанавливаться все последние версии зависимостей, включая сам gulp (при обновлении глобальной версии). Для этого нам необходимо подредактировать файл package.json. В блоке devDependencies, где перечислены установленные расширения вместо версии необходимо указать ключевое слово — . Вот как теперь выглядит мой файл:

Теперь для быстрого разворачивания проекта необходимо сделать следующее — копируем все файлы проекта Gulp за исключением папки node_modules в другую папку вашего проекта. Открываем локально консоль и вводим команду после чего запуститься установка gulp и всех зависимостей. Все, можно начинать работу.

Second: Testing to see if gulp 4 works

Basic Version of your gulpfile:NOTE: Change the gulp.src(…), and .pipe(gulp.dest(…)) paths if you are copying and pasting.

Let’s find out if gulp 4 is working:

  1. run the default gulp task in your terminal:

    • If you see: in your terminal then you know that gulp 4 is installed correctly.
    • If you see «It’s working!» but also some errors, then you will need to resolve those, but the issue should be with your gulpfile.js file. (Also see more recent install/uninstall instructions && more recent gulp 4 tutorials if some time has passed since this has been posted).
    • If you just see errors (and «It’s working!» doesn’t at all appear), then it’s likely that gulp 4 was not properly installed, or gulp 3 was not properly removed and you should uninstall both gulp 3 and 4 and reinstall gulp 4. NOTE: This is especially likely if you have tried a bunch of various install instructions from various sources.
  2. If it worked, then:

    • let’s back up your css files that sass has created for you previously (if existing project), and then deleting those css files, and then
    • next try running your sass task in the terminal:

If it worked (created those files), then everything is working and continue to my other snippets below.

Sample gulpfile.js

This file will give you a taste of what gulp does.

var gulp =require('gulp');var less =require('gulp-less');var babel =require('gulp-babel');var concat =require('gulp-concat');var uglify =require('gulp-uglify');var rename =require('gulp-rename');var cleanCSS =require('gulp-clean-css');var del =require('del');var paths ={  styles{    src'src/styles/**/*.less',    dest'assets/styles/'},  scripts{    src'src/scripts/**/*.js',    dest'assets/scripts/'}};functionclean(){returndel('assets');}functionstyles(){returngulp.src(paths.styles.src).pipe(less()).pipe(cleanCSS()).pipe(rename({      basename'main',      suffix'.min'})).pipe(gulp.dest(paths.styles.dest));}functionscripts(){returngulp.src(paths.scripts.src,{ sourcemapstrue}).pipe(babel()).pipe(uglify()).pipe(concat('main.min.js')).pipe(gulp.dest(paths.scripts.dest));}functionwatch(){gulp.watch(paths.scripts.src, scripts);gulp.watch(paths.styles.src, styles);}var build =gulp.series(clean,gulp.parallel(styles, scripts));exports.clean= clean;exports.styles= styles;exports.scripts= scripts;exports.watch= watch;exports.build= build;exports.default= build;

Motivation/Usage

Ordinarily you’d be fine doing this:

browserify index.js | uglifyjs -c > bundle.js

But uglifyify is able to yield smaller output by processing files individually instead of just the entire bundle. When using uglifyify you should generally also use Uglify, to achieve the smallest output. Uglifyify provides an additional optimization when used with Uglify, but does not provide all of the optimization that using Uglify on its own does, so it’s not a replacement.

Uglifyify gives you the benefit of applying Uglify’s «squeeze» transform on each file before it’s included in the bundle, meaning you can remove dead code paths for conditional requires. Here’s a contrived example:

if(true){module.exports=require('./browser')}else{module.exports=require('./node')}

will be excluded by Uglify, meaning that only will be bundled and required.

if(process.env.NODE_ENV==='development'){module.exports=require('./development')}else{module.exports=require('./production')}

And use this to compile:

NODE_ENV=development browserify -t envify -t uglifyify index.js -o dev.js &&NODE_ENV=production browserify -t envify -t uglifyify index.js -o prod.js

It should go without saying that you should be hesitant using environment variables in a Browserify module — this is best suited to your own applications or modules built with Browserify’s tag.

Global Transforms

You might also want to take advantage of uglifyify’s pre-bundle minification to produce slightly leaner files across your entire browserify bundle. By default, transforms only alter your application code, but you can use global transforms to minify module code too. From your terminal:

browserify -g uglifyify ./index.js > bundle.js

Or programatically:

var browserify =require('browserify')var fs =require('fs')var bundler =browserify(__dirname+'/index.js')bundler.transform('uglifyify',{ globaltrue})bundler.bundle().pipe(fs.createWriteStream(__dirname+'/bundle.js'))

Note that this is fine for uglifyify as it shouldn’t modify the behavior of your code unexpectedly, but transforms such as envify should almost always stay local – otherwise you’ll run into unexpected side-effects within modules that weren’t expecting to be modified as such.

Use latest JavaScript version in your gulpfile

Most new versions of node support most features that Babel provides, except the / syntax. When only that syntax is desired, rename to , install the module, and skip the Babel portion below.

Node already supports a lot of ES2015+ features, but to avoid compatibility problems we suggest to install Babel and rename your to .

npm install --save-dev @babel/register @babel/core @babel/preset-env

Then create a .babelrc file with the preset configuration.

{"presets""@babel/preset-env"}

And here’s the same sample from above written in ES2015+.

importgulpfrom'gulp';importlessfrom'gulp-less';importbabelfrom'gulp-babel';importconcatfrom'gulp-concat';importuglifyfrom'gulp-uglify';importrenamefrom'gulp-rename';importcleanCSSfrom'gulp-clean-css';importdelfrom'del';constpaths={  styles{    src'src/styles/**/*.less',    dest'assets/styles/'},  scripts{    src'src/scripts/**/*.js',    dest'assets/scripts/'}};exportconstclean=()=>del('assets');exportfunctionstyles(){returngulp.src(paths.styles.src).pipe(less()).pipe(cleanCSS()).pipe(rename({      basename'main',      suffix'.min'})).pipe(gulp.dest(paths.styles.dest));}exportfunctionscripts(){returngulp.src(paths.scripts.src,{ sourcemapstrue}).pipe(babel()).pipe(uglify()).pipe(concat('main.min.js')).pipe(gulp.dest(paths.scripts.dest));}functionwatchFiles(){gulp.watch(paths.scripts.src, scripts);gulp.watch(paths.styles.src, styles);}export{watchFilesaswatch};constbuild=gulp.series(clean,gulp.parallel(styles, scripts));exportdefaultbuild;

LICENSE

(MIT License)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the «Software»), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED «AS IS», WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

gulp-watch

gulp-watch это название плагина для Gulp, который отслеживает изменение файлов. Начиная с четвёртой версии Gulp gulp-watch включен в основной пакет и не требует отдельной установки. Начнём с простого — делаем функцию, которая при каждом изменении файла index.html в папке app будет выводить предупреждение.

Как это выглядит в Gulp 4

Чтобы запустить мониторинг пишем

gulp watch

Using gulpfile ~\Desktop\Sites\heihei\gulpfile.js Starting ‘watch’…

Теперь вносим изменения в файл index.html и сохраняем

Starting ‘html’… Кто-то отредактировал index.html!

Как это выглядело в Gulp 3

Создадим папку /app/assets/styles/ , в которой будут файлы .css для разработки Напишем функцию, которая будет собирать все файлы .css из этой папки, обрабатывать их с помощью sass и соединять в один файл /app/temp/styles/style.css Мы уже писали такую функцию выше, просто немного изменим её. Добавим мониторинг файлов CSS gulp.watch( «./app/assets/styles/**/*.css», style);

Теперь как только мы отредактируем один из файлов стилей watch заметит это изменение, пропустит его через sass, соберет все файлы в один. Зачем нужен SASS: чтобы пользоваться css переменными. Создать переменную, которую потом вставлять в .css пропускать через sass compiler и когда вдруг везде нужно будет изменить значение этой переменной, например, на сайте изменится основной цвет, всё что нам нужно будет сделать — это поменять одну переменную в одном файле. чтобы делать вложения в стилях (nested css) чтобы использовать mixins

Use last JavaScript version in your gulpfile

Node already supports a lot of ES2015, to avoid compatibility problem we suggest to install Babel and rename your as .

npm install --save-dev babel-register babel-preset-es2015

Then create a .babelrc file with the preset configuration.

{"presets""es2015"}

And here’s the same sample from above written in ES2015.

importgulpfrom'gulp4';importlessfrom'gulp-less';importbabelfrom'gulp-babel';importconcatfrom'gulp-concat';importuglifyfrom'gulp-uglify';importrenamefrom'gulp-rename';importcleanCSSfrom'gulp-clean-css';importdelfrom'del';constpaths={  styles{    src'src/styles/**/*.less',    dest'assets/styles/'},  scripts{    src'src/scripts/**/*.js',    dest'assets/scripts/'}};constclean=()=>del('assets');export{clean};exportfunctionstyles(){returngulp.src(paths.styles.src).pipe(less()).pipe(cleanCSS()).pipe(rename({      basename'main',      suffix'.min'})).pipe(gulp.dest(paths.styles.dest));}exportfunctionscripts(){returngulp.src(paths.scripts.src,{ sourcemapstrue}).pipe(babel()).pipe(uglify()).pipe(concat('main.min.js')).pipe(gulp.dest(paths.scripts.dest));}exportfunctionwatch(){gulp.watch(paths.scripts.src, scripts);gulp.watch(paths.styles.src, styles);}constbuild=gulp.series(clean,gulp.parallel(styles, scripts));export{build};exportdefaultbuild;

What’s new in 4.0?!

  • The task system was rewritten from the ground-up, allowing task composition using and methods
  • The watcher was updated, now using chokidar (no more need for gulp-watch!), with feature parity to our task system
  • First-class support was added for incremental builds using
  • A method was exposed to create symlinks instead of copying files
  • Built-in support for sourcemaps was added — the gulp-sourcemaps plugin is no longer necessary!
  • Task registration of exported functions — using node or ES exports — is now recommended
  • Custom registries were designed, allowing for shared tasks or augmented functionality
  • Stream implementations were improved, allowing for better conditional and phased builds

Создание проекта

Двигаемся дальше. Теперь создайте папку проекта в удобном месте вашего компьютера. Назовем ее, например, gproject.


Перейдем в папку проекта и запустим консоль команд для данного каталога. Наиболее быстрый вариант сделать это зажать клавишу «Shift» и удерживая ее щелкнуть правой кнопкой мыши на пустой области окна каталога. Откроется контекстное меню, в котором выбираем «Открываем окно PowerShell здесь«. Данный пункт может называться и по другому — «Открыть окно команд«.

Запускаем инициализацию проекта командой:

Заполняем необходимые поля проекта по шагам и желательно латиницей. После ввода названия жмем Enter и переходим с следующему шагу.

  • package-name: вводим название проекта маленькими буквами
  • version: оставляем по умолчанию — 1.0.0
  • description: вводим описание проекта, например, My first gulp project.
  • entry point: (index.js), test command:, git repository:, keywords: — данные шаги оставляем по умолчанию, жмем Enter и переходим к следующему шагу
  • author: впишите имя автора, я ввел Zaur Magomedov
  • license: оставляем по умолчанию
  • Is this ok? — вводим «yes» и жмем Enter поле чего в папке нашего проекта появится файл package.json. 

Файл package.json содержит в себе информацию о проекте + информацию об установленных пакетах (плагинов). Это своего рода файл манифеста. Теперь давайте установим Gulp локально в папку нашего проекта. Для этого пишем следующую команду:

флаг —save-dev как раз говорит установщику установить gulp локально в папку нашего проекта.

При установке gulp название пакета и его версия автоматически пропишутся в файле package.json. Вообще такой подход позволяет сохранять файл package.json со всеми установленными пакетами (зависимости), а при разворачивании нового проекта достаточно скопировать данный файл и запустить команду установки в консоли проекта — и все пакеты в проект установятся автоматически. Сейчас может вам будет не совсем понятно, но дальше я уверен вы поймете.

И так, после установки gulp в папке проекта должна появиться папка node_modules,  она содержит в себе необходимые зависимости. Также все новые установленные зависимости, прописываемые в package.json будут складываться именно в данную папку. Так что ничего не трогайте в ней и не удаляйте. Не пугайтесь если увидите в ней много файлов и папок.

Давайте откроем файл package.json реактором кода и взглянем на его содержимое.

Мы видим всю ту информацию, которую вводили при инициализации проекта + ниже в блоке «devDependencies» указаны установленные зависимости и их версии. В данном случае это gulp версии 3.9.1. Как я и говорил установилась локально именно та версия, что стоит глобально.

Incremental Builds

You can filter out unchanged files between runs of a task using the function’s option and :

constpaths={...  images{    src'src/images/**/*.{jpg,jpeg,png}',    dest'build/img/'}}functionimages(){returngulp.src(paths.images.src,{sincegulp.lastRun(images)}).pipe(imagemin({optimizationLevel5})).pipe(gulp.dest(paths.images.dest));}functionwatch(){gulp.watch(paths.images.src, images);}

Task run times are saved in memory and are lost when gulp exits. It will only save time during the task when running the task for a second time.

If you want to compare modification time between files instead, we recommend these plugins:

functionimages(){var dest ='build/img';returngulp.src(paths.images).pipe(newer(dest)).pipe(imagemin({optimizationLevel5})).pipe(gulp.dest(dest));}

If you can’t simply filter out unchanged files, but need them in a later phase of the stream, we recommend these plugins:

functionscripts(){returngulp.src(scriptsGlob).pipe(cache('scripts')).pipe(header('(function () {')).pipe(footer('})();')).pipe(remember('scripts')).pipe(concat('app.js')).pipe(gulp.dest('public/'))}

Features1: Smart package-require notation

gulpfile.js

var gulp =require('gulp');var less =require('gulp-less');var coffee =require('gulp-coffee');var pleeease =require('gulp-pleeease');var minifyCss =require('gulp-minify-css');var taskListing =require('gulp-task-listing');var minHtm =require('gulp-html-minifier');var uglify =require('gulp-uglify');var rename =require('gulp-rename');var watch =require('gulp-watch');var plumber =require('gulp-plumber');var coffeelint =require('gulp-coffeelint');var concat =require('gulp-concat');

gulpfile.js

var gulp =require('gulp');var pkg =require('gulp-packages')(gulp,'less','coffee','pleeease','minify-css','taskListing','html-minifier as minHtm','uglify','rename','watch','plumber','coffeelint','concat');

For the above example, Packages are loaded into variable.

gulp.src('/path/to/*.coffee').pipe(pkg.plumber()).pipe(pkg.coffee()).pipe(pkg.uglify()).pipe(gulp.dest('js/'));

Incremental Builds

You can filter out unchanged files between runs of a task using the function’s option and :

constpaths={...  images{    src'src/images/**/*.{jpg,jpeg,png}',    dest'build/img/'}}functionimages(){returngulp.src(paths.images.src,{sincegulp.lastRun(images)}).pipe(imagemin({optimizationLevel5})).pipe(gulp.dest(paths.images.dest));}functionwatch(){gulp.watch(paths.images.src, images);}

Task run times are saved in memory and are lost when gulp exits. It will only save time during the task when running the task for a second time.


If you want to compare modification time between files instead, we recommend these plugins:

functionimages(){var dest ='build/img';returngulp.src(paths.images).pipe(newer(dest)).pipe(imagemin({optimizationLevel5})).pipe(gulp.dest(dest));}

If you can’t simply filter out unchanged files, but need them in a later phase of the stream, we recommend these plugins:

functionscripts(){returngulp.src(scriptsGlob).pipe(cache('scripts')).pipe(header('(function () {')).pipe(footer('})();')).pipe(remember('scripts')).pipe(concat('app.js')).pipe(gulp.dest('public/'))}

works great inside lazypipe

Lazypipe assumes that all function parameters are static, gulp-if arguments need to be instantiated inside each lazypipe. This difference can be easily solved by passing a function on the lazypipe step

var gulpif =require('gulp-if');var jshint =require('gulp-jshint');var uglify =require('gulp-uglify');var lazypipe =require('lazypipe');var compressing =false;var jsChannel =lazypipe().pipe(jshint).pipe(jshint.reporter).pipe(jshint.reporter,'fail').pipe(function(){returngulpif(compressing,uglify());});gulp.task('scripts',function(){returngulp.src(paths.scripts.src).pipe(jsChannel()).pipe(gulp.dest(paths.scripts.dest));});

Дополнительные полезные плагины для Gulp

Проект, что мы создали выше это самый простейший набор плагинов Gulp. Его функциональность можно значительно расширить, установив и настроив дополнительные плагины. Я составил список, которые сам использую в своих проектах.

Plumber — дает возможность продолжить работу gulp при ошибке в коде. На самом деле, если вы в коде (html, js, sass, css) сделаете ошибку gulp выдаст ее в консоли и прекратит свою работу. Вам необходимо будет исправить ошибку и вновь запустить gulp.

Sourcemaps — создает карту подключений файлов css и js. Обычно в подобных проектах файлы стилей и скриптов делят на несколько частей для удобства. Так вот, чтобы потом ориентироваться в каком файле и в какой строке находится та или иная запись как раз и нужна карта подключений. 

Tinypng — сжатие изображений. Работает по той же аналогии, что и imagemin, но сжимает значительно лучше.

SvgSprite — сборка svg-спрайта иконок. В последнее время я перешел с иконочных шрифтов на svg иконки. Чтобы уменьшить количество http запросов к серверу нужно собирать иконки в спрайты.

Rigger — объединяет html-файлы в один. Необходимо, когда вы разбиваете html-файлы на отдельные неизменяемые блоки, например, шапка сайта, футер и т.д.

BrowserSync — перезагрузка браузера. Очень полезное дополнение, т.к. не нужно тратить время на обновление браузера, плагин делает это автоматически при сохранении измененных файлов. В плагине используется встроенный простенький локальный-сервер.

Spritesmith — создание спрайтов картинок. Еще один плагин для сборки спрайтов, только в данном случае из иконок Png. Также использую его частенько, т.к. не всегда иконки в макете бывают в формате svg.

Rimraf — очистка папки dist. Бывает, что приходится очищать время от времени папку продакшена dist, т.к. в нем могут скопиться неиспользуемые файлы. Например, вы переименовали файл стилей в другое название, таким образом у вас в dist будут две копии — старая и новая.

На этом я завершаю данную статью, которая и так получилась достаточно объемной. Я сам очень долго изучал эту тему, а инструкции в интернете в основном являлись переводами зарубежных статей, в которых описывалось все скромно.

Конечно я буду еще писать на тему Gulp, т.к. сам постоянно развиваюсь и стараюсь делать свои проекты как можно проще и быстрее. Обязательно поделюсь своей сборкой в Github.

Жду ваши комментарии ниже. Подписывайтесь на мой канал в телеграм. До встречи в следующих статьях!


С этим читают