Narzędzia programisty: Gulp
Ostatnio pisałem o automatyzacji. Jest to proces wymagający dobrania odpowiedniego narzędzia. Dzisiaj skupię się na jednym z nich - Gulp.
Gulp jest jednym z narzędzi służącym do automatyzacji. Strona projektu opisuje go jako streaming build system. W niektórych miejscach widnieje jako task runner. Zwał jak zwał. Wiele nazw na to samo. Gulp jest zwykle używany do automatyzowania webdevelopmentu. Prawdę mówiąc sam używam go do tego celu, jednak można go wykorzystać w dowolny inny sposób. Musisz mieć jedynie zbiór plików, na których chcesz operować oraz opis tego, co chcesz zrobić.
O tym dlaczego warto automatyzować pisałem w poprzednim poście.
Jak to działa?
Gulp jest narzędziem uruchamianym w środowisku node.js. Co prawda sens robienia aplikacji webowych w node jest mocno dyskusyjny, ale narzędzia na tej platformie są przemyślane. Gulp operuje na strumieniach. Oznacza to, że wszystkie stany pośrednie są przechowywane w pamięci, zamiast zapisywania np. tymczasowych plików na dysku. Gulp jest narzędziem konsolowym, jednak w świecie .NET zadania gulpa można go bez problemu uruchomić z poziomu Visual Studio (od wersji 2015). Gulpa można użyć do wielu zastosowań. Najpopularniejszymi z nich są:
- weryfikacja poprawności składni (lintery),
- transpilacja LESS/SASS -> CSS,
- transpilacja TypeScript/ECMAScript* -> JavaScript,
- minifikacja plików CSS/JavaScript/HTML,
- zmiana rozmiarów obrazków i optymalizacja kompresji,
- kopiowanie plików lub budowanie paczki przed deployem,
- deployowanie,
- czyszczenie zbudowanych plików.
Konfiguracja
Pracę z Gulpem rozpoczynamy od instalacji Node.js, npm i samego Gulpa. Node.js i npma najszybciej zainstalujemy za pomocą Chocolatey (Windows), Homebrew (macOS), apt-get (Linux) lub dowolnego innego managera pakietów.
Następnie przechodzimy do instalacji samego Gulpa. Gulp musi być zainstalowany globalnie w systemie oraz w samym projekcie. Przechodzimy do katalogu projektu, który będziemy automatyzować i wykonujemy:
Możemy teraz odpalić gulpa
Oczywiście polecenie się posypało. Nie mamy jeszcze opisu żadnego zadania, które chcemy wykonywać. Opis ten umieszczamy w pliku gulpfile.js.
gulpfile.js
Czas na zrobienie pierwszego zadania! Załóżmy, że chcemy zbudować pliki LESS z katalogu /src/less/, zminifikować je i umieścić w dist/css/. Chcemy, aby właściwości CSS były prefixowane w celu zapewnienia wsparcia w przeglądarkach 2 wersje wstecz. Będziemy potrzebowali do tego kilku pluginów. Z konsoli/terminala wykonujemy:
Mamy wszystko, czego potrzebujemy. Teraz wystarczy dodać plik gulpfile.js:
Teraz wystarczy uruchomić zadanie. Zadanie default jest domyślnym zadaniem gulpa, więc nie musimy podawać jego nazwy. Jeżeli jednak chcemy to zrobić, wpisujemy w konsoli/terminalu:
W ten sposób wygenerował nam się plik main.min.css w katalogu dist/css/.
Był to bardzo prosty przykład. Przyjrzyjmy się czemuś odrobinę bardziej skomplikowanemu.
Zależność zadań
Chcemy zbudować aplikację składającą się z plików LESS, TypeScript i HTML. Chcemy wykonać poszczególne zadania:
- lintowanie (weryfikacja składni) plików LESS,
- lintowanie (weryfikacja składni) plików TS,
- budowanie plików LESS,
- budowanie plików TS,
- minifikacja zbudowanych plików CSS,
- minifikacja zbudowanych plików JS,
- minifikacja plików HTML.
Niektóre zadania są od siebie zależne.
Budowanie plików LESS nie ma sensu, dopóki nie przejdzie ich weryfikacja.
Należy przeanalizować, które z zadań mogą być zastosowane bez uruchamiania innych zadań. Zadania nie mogą przekazywać strumienia pomiędzy sobą. W celu przekazania danych pomiędzy zadaniami konieczne jest zapisanie plików na dysku, a następnie ich ponowne załadowanie.
Weryfikacja składni plików LESS ma sens jako osobne zadanie. Minifikacja plików LESS jest zawsze powiązana z ich budowaniem (możemy generować pliki .css i .min.css).
Żeby zauważyć zależności, najłatwiej będzie narysować skierowany pseudograf zadań:
Zadania, które muszą zostać wykonane przed rozpoczęciem bieżącego zadania podajemy jako drugi parametr funkcji gulp.task():
Obserwowanie zmian w plikach
Pracując nad aplikacją zwykle potrzebujemy ją przebudować po zmianie w plikach źródłowych. Gulp oferuje możliwość śledzenia zmian na plikach, w związku z czym możemy wywołać callback. Warto jest także dodać zadanie budowania aplikacji jako zależność watcha.
Ekstrakcja ścieżek i danych dostępowych
W pliku gulpfile.js znajduje się wiele różnych ścieżek. Część z nich służy do przeszukiwania plików źródłowych. Część jest ścieżką dla plików wynikowych. Do tego dochodzą różnego rodzaju dane dostępowe, np. hasła do ftp, klucze do chmury. Żeby zapanować nad tym chaosem, możemy wydzielić ścieżki i dane dostępowe do odrębnych plików. Osobiście ścieżki wyciągam do gulp.config.js, a dane dostępowe do secrets.json.
gulp.config.js
secrets.json
Po utworzeniu tych plików jesteśmy w stanie uzyskać do nich dostęp w gulpfile.js:
Na deser zostało jeszcze zmniejszenie liczby upierdliwych require. Za pomocą gulp-load-plugins usuwamy wszystkie załączenia pluginów z nazwą gulp-*.
Podsumowanie
W wyniku wszystkich wymienionych operacji dostaliśmy następujący gulpfile:
Nie jest to kod pasujący do każdej sytuacji, ale może stanowić podstawę do Twojego pierwszego skryptu automatyzującego.
Cały projekt umieściłem na Githubie. Możesz się nim pobawić, żeby poczuć w jaki sposób działa Gulp.