GULP czyli automatyzacja pisania kodu. Co to jest i konfiguracja na start.

Front-end i praca z kodem to nie tylko pisanie skryptów, modułów i algorytmów. W tle należy wykonywać jeszcze sporo dodatkowych czynności jak np. przeładowanie przeglądarki czy załadowanie preprocesora CSS. Te rzeczy są monotonne i zajmują dużo czasu. Często za dużo… Gdzie jest Gulp?

Początkujący Developer po relatywnie krótkim okresie dochodzi do podobnego wniosku. Jak sobie z tym poradzić? Użyj narzędzia do automatyzacji lub inaczej task runnera. Jednim z nich jest właśnie Gulp.

Dlaczego GULP?

Kiedy kilka miesięcy temu zaczynałem przygodę z programowaniem szczęśliwie natrafiłem na Front-end Road Map na 2017 rok. Wygląda to właśnie tak:

Jak widać na pewnym etapie znajomość jednego z task runnerów jest opisana jako konieczność. W tym wypadku rekomendowany jest Gulp i postanowiłem się z nim zaznajomić. Jako bezpośrednią alternatywę widzimy Grunta jednak powoli traci on popularność na koszt swojego konkurenta. Dlaczego? Podobno Gulp jest łatwiejszy w konfiguracji i szybszy/lżejszy. Czy to prawda? Nie wiem, ale trafiłem na kilka artykułów z takim właśnie wnioskiem na koniec. Nie wnikam. Skoro tak piszą to biorę się za Gulp’a 🙂

Czym jest Task Runner?

Zanim przejdziemy do instalacji i konfiguracji Gulp’a to wyjaśnię lepiej czym jest task runner. Jest to narzędzie które wykonuje szereg procesów w tle podczas gdy my możemy zająć się pisaniem kodu i sprawdzaniem wyników w tym wypadku przeglądarce bez martwienia się o tzw. pierdoły. Czym są pierdoły? Np:

  • uruchomienie preprocesora CSS np. SASS.
  • automatyczne przeładowanie przeglądarki po zapisaniu kodu.
  • minifikacja kodu JS i CSS czyli zmiejszenie rozmiaru plików.
  • konkatenacja kodu JS i CSS czyli łączenie wielu plików .js lub .css w jeden plik końcowy
  • kompilacja skryptów np. ES6, coffeescript itp. do form obsługiwanych przez przeglądarki.

I wiele, wiele innych…

Jak to się robi? Poszczególne procesy (taski) dodaje się za pomocą tzw. pluginów które należy zainstalować i odpowiednio skonfigurować w pliku konfiguracyjnym Gulp. Następnie odpalamy z terminala i gotowe 🙂

Instalacja i konfiguracja Gulp’a.

Na początek można sobie sprawdzić stronę główną programu:

http://gulpjs.com/

Pierwsza konfiguracja nie jest tak oczywista dla nowego użytkownika i nie obędzie się bez użycia kilku dodatkowych narzędzi które powinny być znane każdemu Developerowi. Jak zwykle wytłumaczę wszystko krok po kroku 🙂

1. Instalacja NodeJS

Pierwszym krokiem jest instalacja pakietu NodeJS. Nie przejmujcie się, nie będziemy tu robić żadnych serwerów. Po prostu potrzebujemy tego do obsługi npm czyli Node Package Managera. Przez npm będziemy później instalować wszystko inne. Noda ściągamy stąd:

https://nodejs.org/en/

2. Instalacja GULP’a globalnie.

Kiedy npm jest gotowy to czas na instalacje Gulp’a globalnie czyli tak jakby na całym kompie 🙂 Robi się to w terminalu wpisując komendę:

npm install gulp -g

dla użytkowników Maca może zadziałać to:

sudo npm install gulp -g

3. Instalacja GULP’a lokalnie 🙂

Teraz robimy instalację lokalną czyli w folderze naszego projektu który trzeba wcześniej stworzyć. Następnie wchodzimy do tego folderu zarówno w naszym edytorze tekstowym jak i w terminalu. Ja używam VS Code i wbudowany tam terminal automatycznie przechodzi do odpowiedniego folderu. Aby dostać się do folderu projektu w terminalu użyj komendy:

cd twoj/folder/z/projektem

Następnie stwórz plik package.json zawierający informacje ogólne. Nic w nim nie zmieniamy, z czasem zrozumiesz o co w nim chodzi. Aby go stworzyć będąc cały czas w folderze projektu w terminalu (bądź tam cały czas) wpisz komendę (i wciśnij tam kilka razy enter):

npm init

Po tym zainstaluj Gulp’a lokalnie. Wpisz w terminalu:

npm install gulp --save-dev

4. Struktura plików w projekcie.

Aby project był przejrzysty i działał prawidłowo należy stworzyć odpowiednią strukturę/nazewnictwo folderów i plików.

Pewnie zauważyłeś/aś, że w projekcie pojawił się nowy folder tzn. node_modules. Tam przechowywane są wszystkie moduły/pluginy zarówno te domyślne jak i te które będą zainstalowane kolejnych krokach. Generalnie to tego folderu się nie rusza bez potrzeby.

Pozostałe foldery będą wyglądały mniej więcej tak:

/- app/                     – na tym folderze będziesz pracował/a

      |- css/                 – tu będą skompilowane pliki .css

      |- fonts/             – tu czcionki

      |- images/          – tu grafiki

      |- index.html     – to wiadome, główny plik .html

      |- js/                    – tu skrypty

      |- scss/                – tu piszesz swoje .scssy, zamienią się potem w .cssy

  |- dist/                     – ten folder ma być pusty. Pojawią się tam finalne pliki projektu po kompilacji dokonanej przez Gulpa. To się wrzuca na serwer (tak mniej więcej).

  |- node_modules/

  |- package.json

  |- gulpfile.js

Kiedy Twój projekt ma już uporządkowaną strukturę można przejść dalej. Oczywiście możesz przyjąć inną konwencję. Nic Ci nie stoi na przeszkodzie jednak pamiętaj aby dopasować potem wszystkie ścieżki.

5. Stworzenie pliku konfiguracyjnego gulpfile.js

Teraz czas na stworzenie pliku konfiguracyjnego o nazwie gulpfile.js w folderze głównym projektu (plik równorzędny np. z package.json). Owy plik jest sercem Gulp’a i wszystko co będzie się działo potem będzie się kręciło właśnie wokół tego pliku. Jak nie trudno się domyśleć przy jego tworzeniu przydatna jest znajomość JSa 🙂

Zasadniczo plik składa się z dwóch części tzn. pierwsza gdzie na górze deklarujemy zmienne które importują moduły odpowiedzialne za taski, w drugiej części definiujemy poszczególne taski.

Na początek podstawowy plik powinien wyglądać w ten sposób:

//Deklaracja zmiennych
var gulp = require('gulp');

//Definicja tasków
gulp.task('hello', () => {
console.log('Hello Bartek');
});

Co wykonuje powyższy kod? Importuje moduł gulp i tworzy task hello który wyrzuca komunikat w terminalu 🙂 Wpisz tam:

gulp hello

Jeśli widzisz console.loga to znaczy, że wszystko działa 🙂 Czas na mięso czyli dodanie właściwych tasków 🙂

6. Dodawanie tasków.

Zrobię to w ten sposób, będę dodawał do kodu kolejne taski które uważam za pożyteczne i w konsekwencji powstanie kompletny plik gulpfile.js który można użyć w Waszych projektach projektach 🙂 Pełny kod będzie na końcu.

Task 1 – Automatyczne przeładowanie przeglądarki.

Proste, nie wymaga komentarza. Najpierw zainstaluj moduł przy użyciu npm, potem dodaj kod:

npm install browser-sync --save-dev
//Deklaracja zmiennych
var browserSync = require('browser-sync').create();

//Definicja tasków
gulp.task('browserSync', function () {
  browserSync.init({
    server: {
    baseDir: 'app'
    },
  })
})

Task 2 – SASS

Używasz SASSa który kompilowany jest potem do pliku .css. Zainstaluj moduł i dodaj kod:

npm install gulp-sass --save-dev
//Deklaracja zmiennych
var sass = require('gulp-sass');

//Definicja tasków
gulp.task('sass', () => {
  return gulp.src('app/sass/**/*.scss')
  .pipe(sass()) // Converts Sass to CSS with gulp-sass
  .pipe(gulp.dest('app/css'))
  .pipe(browserSync.reload({
    stream: true
    }))
});

Task 3 – Optymalizacja grafiki

Ten task optymalizuje pliki graficzne i w konsekwencji zmniejsza ich rozmiar. Uważam, że ten moduł działa średnio i optymalizacja grafiki np. za pomocą TEGO  portalu działa dużo lepiej. Nie mniej jednak lepszy rydz niż nic 🙂

npm install gulp-imagemin —save-dev

npm install gulp-cache --save-dev
//Deklaracja zmiennych
var imagemin = require('gulp-imagemin');
var cache = require('gulp-cache');

//Definicja tasków
gulp.task('images', function () {
  return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)')
  .pipe(cache(imagemin({
    interlaced: true
   })))
  .pipe(gulp.dest('dist/images'))
});

Task 4 – Obsługa fontów

//Definicja tasków
gulp.task('fonts', function () {
  return gulp.src('app/fonts/**/*')
  .pipe(gulp.dest('dist/fonts'))
})

Task 5 – Watch

Czyli nasłuchiwanie zmian w poszczególnych plikach np. .html w celu wykonania poszczególnych zadań. Ja ustawię to aby wykonywał się SASS i przeładowanie przeglądarki.

//Definicja tasków
gulp.task('watch', ['browserSync', 'sass'], () => {
  gulp.watch('app/sass/**/*.scss', ['sass']);
  gulp.watch('app/*.html', browserSync.reload);
  gulp.watch('app/js/**/*.js', browserSync.reload);
});

Task 6 – Konkatenacja i minifikacja CSS

Tutaj połączymy pliki .css jeśli mamy takowych wiele i usuniemy różne spacje itp. aby finalny plik był mniejszy. W tym wypadku użyjemy modułu m.in. useref. Tradycyjnie już instalujemy moduły z npm a potem dodajemy kod do gulpfile.js:

npm install gulp-useref --save-dev

npm install gulp-cssnano
//Deklaracja zmiennych
var useref = require('gulp-useref');
var gulpIf = require('gulp-if');
var cssnano = require('gulp-cssnano');

//Definicja tasków
gulp.task('useref', () => {
  return gulp.src('app/*.html')
  .pipe(gulpIf('*.css', cssnano()))
  .pipe(gulp.dest('dist'))
});

To jeszcze nie koniec bo do naszego pliku .html trzeba dodać jeszcze formułę:

<!--build:css css/styles.min.css-->

<link rel="stylesheet" href="css/styles.css">

<link rel="stylesheet" href="css/another-stylesheet.css">

<!—endbuild—>

Pomiędzy tymi “komentarzami” powinny znaleść się wszystkie pliki które chcemy połączyć w jedną całość.

Task 7 – Konkatynacja i minifikacja JS

W tym wypadku można by użyć useref jak przy CSS jednak potrzebujemy czegoś co obsługuje ES6. Z pomocą przychodzi moduł babili 🙂

npm install gulp-util --save-dev

npm install gulp-concat --save-dev

npm install gulp-babili --save-dev
//Deklaracja zmiennych
var gutil = require('gulp-util');
var concat = require('gulp-concat');
const babili = require("gulp-babili");

//Definicja tasków
gulp.task('scripts', function () {
  return gulp.src(['app/js/*.js'])
  .pipe(concat('main.min.js'))
  .pipe(babili({
    mangle: {
      keepClassNames: true
    }
  }))
  .on('error', function (err) {
    gutil.log(gutil.colors.red('[Error]'), err.toString());
  })
  .pipe(gulp.dest('dist/js'));
})

Task 8 – Czyszczenie katalogu dist

Pliki często się zmieniają i żeby nie nadpisywać lub zachowywać starych niepotrzebnych rzeczy dobrze wyczyścić folder dist zanim zostaną do niego skompilowane nowe pliki. Robi się to w ten sposób:

npm install del --save-dev
//Deklaracja zmiennych
var del = require('del');

//Definicja tasków
gulp.task('clean:dist', () => {
  return del.sync('dist');
})

Task 9 – Automatyczne wykonywanie tasków

To jak już mamy te wszystkie taski to pasuje je jakoś wywołać automatycznie bo bez sensu jest wykonywanie tylko jednego na raz. Do tego potrzebujemy modułu sekwencji:

npm install run-sequence --save-dev

Teraz zdefiniujmy co się będzie działo po wywołaniu w terminalu prostej komendy gulp:

gulp

Otóż chcę aby wywoływały mi się tylko podstawowe taski tzn. przeładowanie przeglądarki, SASS, i watch.

//Deklaracja zmiennych
var runSequence = require('run-sequence');

//Definicja tasków
gulp.task('default', function (callback) {
  runSequence(['watch', 'sass', 'browserSync'],
    callback
  )
})

Skoro mam już działające podstawowe funkcję to kiedy użyć tych bardziej złożonych? Stanie się to po wpisaniu komendy:

gulp build

Ale na początku:

//Definicja tasków
gulp.task('build', function (callback) {
   runSequence('clean:dist', ['default', 'images', 'fonts'], 'useref', 'scripts',
     callback)
})

Podsumowanie tasków

Powyżej wypisałem kilka podstawowych tasków jednak Gulp to o wiele więcej. Nie mniej jednak ta wiedza wystarczy początkującemu developerowi aby znacząco usprawnić swoją pracę.

Dla leniwych poniżej wklejam kod kompletnego pliku gulpfile.js. Jeżeli zachowaliście tą samą strukturę plików jak ja i zainstalowaliście odpowiednie moduły to po skopiowaniu tego kodu możecie zaczynać zabawę z gulpem na własnym projekcie.

var gulp = require('gulp');
var sass = require('gulp-sass');
var browserSync = require('browser-sync').create();
var useref = require('gulp-useref');
var gulpIf = require('gulp-if');
var cssnano = require('gulp-cssnano');
var imagemin = require('gulp-imagemin');
var cache = require('gulp-cache');
var del = require('del');
var runSequence = require('run-sequence');
var gutil = require('gulp-util');
var concat = require('gulp-concat');
var babili = require('gulp-babili');


gulp.task('hello', () => {
  console.log('Hello Bartek');
});

gulp.task('browserSync', () => {
  browserSync.init({
    server: {
      baseDir: 'app'
    },
  })
})

gulp.task('sass', () => {
  return gulp.src('app/sass/**/*.scss')
    .pipe(sass()) // Converts Sass to CSS with gulp-sass
    .pipe(gulp.dest('app/css'))
    .pipe(browserSync.reload({
      stream: true
    }))
});

gulp.task('images', () =>) {
  return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)')
    .pipe(cache(imagemin({
      interlaced: true
    })))
    .pipe(gulp.dest('dist/images'))
});

gulp.task('fonts', () => {
  return gulp.src('app/fonts/**/*')
    .pipe(gulp.dest('dist/fonts'))
})

gulp.task('watch', ['browserSync', 'sass'], () => {
  gulp.watch('app/sass/**/*.scss', ['sass']);
  gulp.watch('app/*.html', browserSync.reload);
  gulp.watch('app/js/**/*.js', browserSync.reload);
});

gulp.task('useref', () => {
  return gulp.src('app/*.html')
    .pipe(useref())
    .pipe(gulpIf('*.css', cssnano()))
    .pipe(gulp.dest('dist'))
});

gulp.task('scripts', () => {
  return gulp.src(['app/js/*.js'])
    .pipe(concat('main.min.js'))
    .pipe(babili({
      mangle: {
        keepClassNames: true
      }
    }))
  .on('error', function (err) {
     gutil.log(gutil.colors.red('[Error]'), err.toString());
   })
  .pipe(gulp.dest('dist/js'));
})

gulp.task('clean:dist', () => {
  return del.sync('dist');
})

gulp.task('default', function (callback) {
  runSequence(['watch', 'sass', 'browserSync'],
    callback
  )
})

gulp.task('build', function (callback) {
  runSequence('clean:dist', ['default', 'images', 'fonts'], 'useref', 'scripts',
    callback)
})

Gdzie znaleść więcej informacji?

Jak zwykle, internet jest pełen wiedzy. Ja podczas pierwszej konfiguracji korzystałem z TEGO samouczka.  Hehe i mówią, że podobno Gulp jest łatwy w konfiguracji 😛

Podsumowanie

Uff, udało mi się dobrnąć do końca tego technicznego wpisu… Ciężko się to pisze…

Uważam, że warto a nawet trzeba używać narzędzi do automatyzacji. Gulp jest jednym lecz nie jedynym z nich. W następnych artykułach napiszą trochę o Webpacku gdyż to właśnie on zdobywa teraz ogromną popularność 🙂 Ciekawe dlaczego tak się dzieje?

  • An

    polecam jeszcze plugin gulp-load-plugins , który skróci ilość pisywanych bibliotek do pliku gulpfile

  • Paulina Kaczmarek

    dzięki za grafikę z roadmapą, szukałam tego ostatnio, ale nie potrafiłam znaleźć 🙂

  • Hej. Na start linijka 37:
    gulp.task(‚images’, () =>) {
    Jest tam błąd – za dużo o jeden nawias 😉

    Pozatym bardzie ciekawy workflow – zmieniłem troszke i udoskonaliłem dzięki temu mój stary workflow. Dzieki wielkie !!