Gulp dla początkujących

Przy okazji tworzenia gry tekstowej w angularze, wspomniałem o narzędziu gulp. Wykorzystywałem je do automatyzacji pewnych czynności związanych z budowaniem projektu.

Nie objaśniałem wtedy dokładnie jak korzystać z gulpa. Obiecałem jednak, że zrobię to innym razem. W dzisiejszym wpisie zajmę się właśnie tym tematem.

gulp dla poczatkujacych

Tak jak napisałem wyżej, gulp to narzędzie, które pomaga developerowi zautomatyzować żmudne i czasochłonna zadania związane z tworzeniem projektu. Przykłady zadań, które może robić za nas gulp to: minifikowanie skryptów, łączenie plików, odświeżanie strony, dołączanie plików js do dokumentów html czy przenoszenie plików do folderów docelowych. A to tylko część zastosowań.

Kto choć trochę pracował w web developmencie, wie że zadania te mogą być bardzo upierdliwe. Gulp to bardzo przydatne narzędzie.

No dobra, ale jak zacząć z niego korzystać? Przede wszystkim potrzebny będzie do tego node. To właśnie w tym środowisku, będą uruchamiane zadania Gulpa. Opis o tym jak zainstalować node’a i jak korzystać z jego managera paczek – npm, znajdziesz w poprzednich moich wpisach.

Gulpa należy zainstalować używając właśnie npma. Jeżeli wcześniej nie korzystałeś z tego menagera zadań, najpierw musisz zainstalować go globalnie. Należy zrobić to w konsoli, używając tego polecenia:

Dzięki temu gulp będzie dostępny przez zmienną środowiskową. Teraz można już zainstalować gulpa wewnątrz projektu, w którym będzie używany. Najpierw oczywiście trzeba utworzyć plik package.jsonw folderze projektowym (instrukcja jak to zrobić, dostępna jest tutaj). Teraz aby zainstalować gulpa w projekcie wystarczy wprowadzić w konsoli (oczywiście będąc w odpowiednim folderze) takie polecenie:

W ten sposób, gulp zostanie zainstalowany jako zależność developerska. Teraz można zabierać się za tworzenie pierwszych udogodnień. Wszystkie zadania gulpa pisane się w specjalnym pliku – gulpfile.js, nazwa musi być dokładnie taka jak tu.

Tworzę taki plik, ale póki co zostaje on pusty. Zanim przejdę do pisania zadań, muszę przygotować „projekt”. Używam cudzysłowia, bo oczywiście będą to tylko pliki poglądowe 😉 . Wewnątrz głównego katalogu projektowego tworzę folder src. W nim będę trzymał wszystkie pliki „źródłowe”, w stanie developerskim. Wewnątrz src dodaję dwa pliki funk1.js oraz funk2.js. Oto ich zawartość:

funk1.js:

funk2.js:

Tak jak pisałem, nic ambitnego 🙂 to tylko przykłady. Załóżmy, że chcę tworzyć funkcje w osobnych plikach. Dzięki temu łatwiej zarządza się dużą ilością kodu. Jednak dodawanie wielu plików do dokumentu html nie jest zalecane. Lepiej jest najpierw połączenie treści wielu plików w jednym. Użyję do tego gulpa.

Oto jak w tej chwili wygląda struktura projektu (bez katalogu node_modules i pliku package.json):

Chcę, aby moje nowe gulpowe zadanie stworzyło nowy folder dist (od distribution) i dodało do niego nowy plik final.js zawierający połączoną treść plików funk1.js i funk2.js.

Gulp jest narzędziem bardzo modularnym. Sama „czysta” instalacja, nie ma w sobie funkcji do wykonywania konkretnych zadań a jedynie podstawowe mechanizmy. Dzięki temu, możemy dodać do projektu tylko te elementy gulpa, które są potrzebne. Takim elementem, który pomoże w łączeniu plików jest gulp-concat. Muszę zainstalować go w projekcie:

Dobra, teraz mogę działać. Do pliku gulpfile.js mogę dodać pierwszą treść:

I to całe zadanie 🙂 . Jeżeli wygląda ono jak zwykły skrypt JSowy, to nic dziwnego, bo tym własnie jest 🙂 . Pierwsze dwie linijki to dodanie modułów. Głównego gulp oraz potrzebnego do zadania gulp-concat.

Następnie, wywołuję metodę task głównego obiektu gulp. To właśnie ona tworzy zadania. W tym wypadku przyjmuje ona dwa argumenty (może więcej ale o tym za chwilę). Pierwszy argument to nazwa taska. Będzie ona używana do wywoływania zadania w konsoli. Drugi argument to funkcja zawierająca treść zadania. To ta treść jest najważniejsza.

Tworzone w gulpie zadania mają zawsze ten sam schemat. Można go podzielić na trzy części. Pierwszą linijkę, ostatnią i to co jest pomiędzy 🙂 . Pierwsza linijka to wywołanie metody src głównego obiektu gulp. Jako argument przyjmuje ona łańcuch znaków zawierający relatywną ścieżkę do plików zawierających dane, których chcemy użyć. Jak widać można używać w tej ścieżce znaków specjalnych. Ja pobieram wszystkie pliki JS znajdujące się w katalogu src.

Na tym co zwraca metoda src wywołuję metodę pipe, czyli potok. Jest to bardzo ważny element gulpa i postaram się go w miarę jasno wytłumaczyć (oczywiście nie ma opcji abym przedstawił wszystko na temat potoków, więc zachęcam do zgłębienia tematu na własną rękę). W skrócie, potok pobiera przekazane mu przez poprzednią metodę dane, wykonuje na nich operację zdefiniowaną w jego argumencie i przekazuje je dalej.

W moim wypadku jako argument przekazuję wywołanie metody concat z modułu gulp-concat. Metoda ta otrzymuje dane przekazane do potoku (w tej chwili jest to treść plików funk1.js oraz funk2.js) i łączy je w jeden plik, którego nazwę definiuję w argumencie tej metody (final.js)

Wynik tych operacji przekazywany jest dalej do kolejnego potoku znajdującego się w ostatniej linijce. Tak jak zaznaczyłem wyżej, jest to wyjątkowy fragment kodu, zamykający zadanie. Potok ten jako argument otrzymuje bowiem wywołanie metody dest obiektu gulp. Metoda ta, sprawia, że otrzymane przez potok dane wysyłane są do lokacji, zdefiniowanej w jej argumencie. Jeżeli lokacja taka nie istnieje, gulp tworzy odpowiednie foldery.

Aby uruchomić to zdanie wystarczy w koncoli, będąc w katalogu projektowym, wpisać gulp + nazwa_zadania. Wpisuję więc:

I gotowe 🙂 teraz folder projektowy wygląda tak:

Gulp dodał folder dist a wewnątrz utworzył plik final.js, którego treść wygląda tak:

Dużo wygodniej, niż przeklejanie treści ręcznie 🙂 . Ale to nie koniec, piękno gulpa polega między innymi na tym, że jego potoki można łączyć w wiele razy zanim skieruje się je do celu. Powiedzmy, że po połączeniu plików w jeden, zanim zapiszę wynik do folderu docelowego, chcę go zminifikować. I z tym gulp radzi sobie bardzo dobrze.

najpierw muszę jednak do projektu doinstalować odpowiedni moduł. Ten, który potrzebuję nazywa się gulp-uglify. Dodanie go nie powinno sprawiać już żadnych problemów:

Teraz muszę wprowadzić zmiany w moim gulpfile:

Nie zmieniło się wiele. Przede wszystkim dodałem do skryptu nowy moduł ufglify. Druga zmiana pojawia się po linijce zawierającej potok łączący pliki. Wynik tego łączenia zamiast do zakończenia zadania, przekazuję do nowego potoku, w którym wywołuję ugfliy. W wyniku tego działa, treść pliku jest modyfikowana. Dopiero po tym, dane „płyną” do gulp.dest.

teraz wystarczy wywołać ponownie zadanie concat. Jeśli gulp znajdzie już plik final.js, po prostu go nadpisze. Tak będzie wyglądać nowa treść pliku:

I wszystko wygląda super. Minifikator usunął białe znaki, komentarze i zbędne użycie słówka var. Sukces.

Czasem jednak, nie ma możliwości / sensu upchania wszystkiego w jedno zadanie. Przykładowo, do projektu, do katalogu src dodać mogę nowy plik: funk.ts. Rozwinięcie pliku sugeruje, że jest to plik Typescript, czyli coś w rodzaju „dialektu” Javascriptu. Nie można go od razu uruchomić w przeglądarce, najpierw musi zostać „przetłumaczony” na zwykły JS. Tak wygląda jego zawartość:

Chciałbym aby plik ten został połączony z dwoma poprzednimi. Jednak nie mogę tego zrobić od razu, muszę najpierw go skompilować. I w tym pomoże mi gulp. Stworzę osobne zadanie, które pobierze zawartość funk.ts, zamieni ją na Javascript i zapisze do pliku funk.js.

Pierwszą rzeczą jaką potrzebuję to, oczywiście, moduł gulpa mający możliwość przetwarzania typescirptu. Moduł taki nazywa się gulp-script. Instaluje go tak samo jak poprzednie, wywołując w konsoli odpowiednie polecenie:

Teraz w gulpfile, muszę stworzyć odpowiednie zadanie:

Zaczynam od dodania do skryptu odpowiedniego modułu. Następnie tworzę nowe zadanie, które nazywam po postu typescript. Wewnątrz pobieram pliki z rozwinięciem ts i wrzucam je do potoku, który używa modułu gulp-typescript. W argumencie przekazuję obiekt, który znalazłem w dokumentacji. Jego pole out zawiera nazwę pliku wyjściowego. Gotowe dane przekazuję do potoku, który zapisuję wynik z powrotem do katalogu src.

po wywołaniu zadania typescript, wewnątrz folderu src pojawia się nowy plik funk.js. Tak wygląda jego treść:

Gotowe. Plik typescript został przetłumaczony na „nasze” 🙂 . Teraz mogę wywołać zadanie concat i wszystko będzie gotowe tak jak tego chciałem. Ale to wciąż nie koniec. Mogę jeszcze trochę usprawnić proces budowania tego projektu.

Wystarczy, że zmienię jedną linijkę, wywołanie metody task podczas tworzenia zadania concat:

Między nazwą zadania a funkcją zawierającą jego treśc, dodaje jeszcze jeden argument. Jest to tablcia, która zawiera nazwy zadań, wymaganych do uruchomienia tego zadania. Teraz za każdym razem gdy odpalę concat, zanim zostanie uruchomione, gulp najpierw wywoła zadanie typescript.

Odpalam zadanie i sprawdzam zawartość final.js:

Kolejny sukces! Wszystko działa jak należy. Tyle operacji wykonane przy pomocy zaledwie jednego wywołania w konsoli 🙂 .

Jednak nie jest to jeszcze sytuacja idealna. Po pierwsze, w katalogu src został artefakt po wykonanym zdaniu, czyli plik funk.js. Można by było się go pozbyć. Innym udogodnieniem byłaby możliwość automatycznego wywoływania zadań, gdy tylko zawartość któregoś z plików z katalogu src zmieni się. Ponieważ ten post jest już dość długi, opiszę dokładnie jak to zrobić w kolejnym 🙂 .

Tymczasem, jak zawsze, zachęcam do polubienia mojej strony na facebooku. Zawsze na bieżąco zamieszczam tam informacje o nowościach, więc warto polubić aby nie przegapić żadnego nowego wpisu.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *