Ostatnie dwa wpisy poświęciłem narzędziom niezbędnym w pracy front-end developera. Mieliśmy więc już na tapecie podstawy konfiguracji Webpack oraz wpis na temat użycia Webpacka oraz Gulpa razem. Myślę więc, że naturalną koleją rzeczy będzie wpis, w którym przedstawię Wam podstawy konfiguracji Gulp. Stąd też pomysł właśnie na taki wpis w dniu dzisiejszym ;) Zapraszam do lektury!

P. S. Jeśli nie słyszałeś nigdy wcześniej o Gulpie, to spieszę z wyjaśnieniem co to takiego! Mówiąc w skrócie, jest to narzędzie pozwalające na automatyzację zadań w projekcie. Możemy więc wykorzystać go na przykład do automatycznego “transpilowania” stylów napisanych w Sass na czysty CSS, kopiowania plików do odpowiednich katalogów czy uruchamiania lintera. Wszystko to możemy skonfigurować tak aby uruchamiało się za pomocą jednej komendy. Możliwe jest również, obserwowanie zmian tzn., że zadania moga być uruchamiane automatycznie za każdym razem, gdy jakiś plik w projekcie się zmieni.

Instalacja

Zanim zobaczymy jakie są podstawy konfiguracji Gulp myślę, że warto rzucić okiem na to jak w ogóle zainstalować Gulp w naszym projekcie. Dzięki nodejs i jego menedżerowi pakietów NPM, instalacja czegokolwiek we front-endzie jest bardzo prosta. Wszystko co trzeba zrobić to uruchomić poniższą komendę w konsoli. Użytkownicy Linuxa lub OSX/macOS być może będą musieli dodatkowo użyć komendy sudo.

sudo npm install -g gulp

Powyższa komenda zainstaluje nam Gulp globalnie. Jeśli jednak teraz spróbowałbyś użyć komendy gulp, dostałbyś błąd mówiący, że lokalna wersja Gulpa nie została odnaleziona. To dlatego, że oprócz Gulpa zainstalowanego globalnie, należy go jeszcze zainstalować lokalnie w naszym projekcie. Aby to zrobić należy przejść do głównego katalogu projektu i odpalić poniższą komendę:

npm install --save-dev gulp

Super! Teraz jeśli uruchomimy komendę gulp, nie dostaniemy na twarz błędu. Zamiast tego Gulp poinformuje nas, że nie znalazł pliku “gulpfile”. To oznacza, że jesteśmy już blisko zakończenia procesu instalacji. Ostatnia rzecz do zrobienia to stworzenie właśnie tego pliku. Dodajmy więc w głównym katalogu projektu plik gulpfile.js. Poniżej jego przykładowa, minimalna zawartość (jak go rozszerzyć dowiesz się w dalszej części wpisu):

var gulp = require('gulp');

gulp.task('default', function() {
  // place code for your default task here
});

Jeśli czytałeś mój poprzedni wpis to pewnie już coś podobnego widziałeś. Tak - gulpfile.js to jest właśnie plik, w którym będziemy zaraz wszystko konfigurować!

Podstawy konfiguracji Gulp

Ogólnie rzecz biorąc Gulp oparty jest na wtyczkach. Jeśli więc chcemy stworzyć zadanie z użyciem Gulpa, musimy zainstalować odpowiedni plugin. Powiedzmy, że chcielibyśmy na przykład skonfigurować transformację styli napisanych za pomocą LESS do pliku CSS. Aby to zrobić musimy zainstalować wtyczkę gulp-less. Do tego celu użyjemy oczywiście NPM. Najlepiej jest to zrobić lokalnie dla danego projektu:

npm install --save-dev gulp-less

Definicja zadania

Teraz czas przejść do konfiguracji zadania. W tym celu musimy wyedytować utworzony przez nas wcześniej plik gulpfile.js (poniżej prezentuję przykład z dokumentacji wtyczki gulp-less):

var
  gulp = require('gulp'),
  less = require('gulp-less'),
  path = require('path');

gulp.task('less', function () {
  return gulp.src('./layout/**/*.less')
    .pipe(less({
      paths: [ path.join(__dirname, 'less', 'includes') ]
    }))
    .pipe(gulp.dest('./public/css'));
});

Jak widzicie, na początku należy zaimportować niezbędne wtyczki za pomocą require. Jest to sposób na ładowanie modułów wbudowany w Node.js a wywodzi się z CommonJS. Jedną z importowanych wtyczek jest oczywiście zainstalowany przez nas wcześniej gulp-less. Pozostałe wtyczki nie wymagają dodatkowej instalacji.

W linii numer sześć rozpoczynamy definicję zadania poprzez użycie funkcji task. Do funkcji tej przekazujemy dwa parametry: nazwę zadania oraz funkcję wywołania zwrotnego, gdzie implementujemy to zadanie.

W kolejnej linii przekazujemy Gulpowi ścieżkę do plików LESS. Ścieżka taka może zawierać znaki wieloznaczne (ang. wildcards). W naszym przypadku wskazujemy na folder layout. Za pomocą znaków ** informujemy Gulp, że plików szukać ma też w podkatalogach. Informujemy go też, że interesują nas tylko pliki z rozszerzeniem less (*.less).

Transformacje

Idźmy dalej i spójrzmy teraz na linię numer osiem. Tutaj wykonujemy transformację plików LESS na pliki CSS. To co tutaj istotne to użycie funkcji pipe. Jak być może wiecie, Gulp pracuje na strumieniach. To znaczy, że poszczególne transformacje wykonywane w pamięci, a ich rezultat jest przekazywany dalej, do kolejnej transformacji. To co przekazujemy do funkcji pipe to właśnie taka pojedyncza jednostka transformacji. Zwykle jest to, tak jak i w naszym przypadku, wywołanie jakiejś wtyczki.

Samej konfiguracji wtyczki gulp-less nie będę tutaj wyjaśniać ponieważ, nie jest to przedmiotem tego wpisu. Dla ciekawskich odsyłam na stronę dokumentacji wtyczki.

Na koniec, w linii numer jedenaście widzimy kolejne wywołanie funkcji pipe, czyli de facto kolejna transformacja. Tym razem przekazujemy do niej wywołanie funkcji gulp.dest(). W ten sposób przekierowujemy strumień do pliku wynikowego.

Być może zwróciliście już uwagę, że zadanie w Gulp sprowadza się do zdefiniowania łańcucha wywołań funkcji pipe. To jedna z zalet tego narzędzia. Tworzenie zadania jest to po prostu definicja jego kolejnych kroków w stylu “fluent”.

Uruchamianie zadania

Ok. Mamy już skonfigurowane zadanie. Czas je uruchomić! Aby tego dokonać wystarczy w konsoli wywołać komendę gulp z parametrem będącym nazwą zadania:

gulp less

Jako rezultat wywołania powyższej komendy powinniśmy otrzymać nowy podkatalog w głównym katalogu projektu. Jak się domyślacie, ma on nazwę public i zawiera podkatalog css gdzie znajdują się wynikowe pliki CSS po transformacji z LESS.

Zadanie domyślne i kolejkowanie zadań

Absolutne podstawy konfiguracji Gulp mamy już za sobą. Myślę jednak, że warto dodać do tego co już napisałem, kilka dodatkowych informacji. Po pierwsze Gulp umożliwia zdefiniowanie zadania domyślnego:

gulp.task('default', function() {
  // ...
});

Takie zadanie nie wymaga przy uruchamianiu podawania jego nazwy. Wystarczy więc w konsoli wywołać komendę gulp i właśnie to zadanie zostanie wywołane (oczywiście jeśli istnieje).

Częstą praktyką jest wykorzystanie tego zadania do kolejkowania innych zadań. Robi się to poprzez wykorzystanie przeciążonej metody gulp.task(), która przyjmuje trzy parametry:

gulp.task('default', ['less'], function() {
  // ...
});

Jak widać, tym razem jako drugi parametr przekazujemy tablicę. Tablica ta zawiera nazwy zadań, które muszą zostać wywołane przed wywołaniem danego zadania (tutaj zadanie default). Czyli teraz, jeśli wywołamy komendę gulp, najpierw wywołane zostanie zadanie less, a następnie zadanie default.

Tutaj jeszcze ważna uwaga: zadania przekazane w tablicy są wywoływane równocześnie. Należy mieć to na uwadze jeśli pracują one, na przykład, na tych samych plikach.

Watchery plików

Wydaje mi się, że podstawy konfiguracji Gulp powinny zawierać jeszcze jedną istotną sprawę. Chodzi o coś co nazywa się watchery plików (lub bardziej z angielska: file watchery). Za chwile przekonacie się jak przydatna jest to funkcja!

Generalnie to co już osiągnęliśmy z zadaniem less jest całkiem ciekawe. Jednak zauważcie, że w tym momencie musimy uruchamiać zadanie gulp less za każdym razem kiedy zmienimy coś w pliku LESS. Można to jednak zautomatyzować za pomocą wspomnianych właśnie file watcherów. Spójrzmy na modyfikację naszego wcześniejszego przykładu konfiguracji:

var
  gulp = require('gulp'),
  less = require('gulp-less'),
  path = require('path');

gulp.task('less', function () {
  return gulp.src('./layout/**/*.less')
    .pipe(less({
      paths: [ path.join(__dirname, 'less', 'includes') ]
    }))
    .pipe(gulp.dest('./public/css'));
});

// Rerun the task when a file changes
gulp.task('watch', ['less'], function() {
  gulp.watch('./layout/**/*.less', ['less']);
});

To co się zmieniło znajdziecie w linii piętnastej. Jak widzicie, mamy tutaj dodatkowe zadanie watch. Zależy ono od zadania less, więc wszystko co znajduje się w funkcji wywołania zwrotnego zostanie wykonane dopiero gdy zadanie less się zakończy.

Spójrzmy teraz na funkcję wywołania zwrotnego. Wywołujemy w niej funkcję gulp.watch() z dwoma parametrami. Pierwszy z nich to ścieżka do plików źródłowych - w naszym przypadku ta sama jak dla zadania less. Chcemy przecież obserwować zmiany w tych samych plikach. Drugi parametr to tablica zawierająca listę nazw zadań. W naszym przykładzie jest to zadanie less.

Teraz, jeśli wywołamy zadanie watch

gulp watch

… najpierw zostanie wywołane zadanie less a następnie zadanie watch, które na razie nie zrobi nic ale się nie zakończy. Zamiast tego będzie ono monitorować zmiany w plikach LESS w podanej ścieżce. Kiedy któryś z plików ulegnie zmianie, zostanie ponownie wywołane zadanie less.

Aby zakończyć zadanie watch, wystarczy użyć kombinacji klawiszy ctrl + c.

Podsumowanie

Myślę, że to co Wam dziś pokazałem to absolutne podstawy konfiguracji Gulp. Warto je znać ponieważ Gulp to wciąż jedno z najważniejszych narzędzi w arsenale front-end developera. Istnieje prawie 2000 gotowych do użycia wtyczek, więc myślę, że dość łatwo można dzięki niemu osiągnąć cokolwiek potrzebujemy.