Gulp i automatyzacja pracy z szablonem Wordpress | DailyWeb.pl

Gulp i automatyzacja pracy z szablonem WordPress

Opublikowano 1 rok temu - 7


Stare porzekadło mówi, że dobry programista to leniwy programista. Skoro dana czynność jest powtarzalna, to dlaczego miałby zaprzątać sobie tym głowę? Niech zajmie się tym automat! Nie inaczej jest podczas pracy nad szablonami dla WordPressa. Tu z pomocą przychodzi Gulp - task runner napisany w Node.js.

Gdybyśmy mieli wyliczyć zadania, które mogą (lub mogłyby) występować podczas tworzenia szablonu, byłoby ich zapewne całkiem sporo - kompilacja plików SASS, minifikowanie skryptów, optymalizacja obrazów, podbijanie wersji w pliku style.css, tworzenie wersji produkcyjnej, czy wysyłanie plików na serwer. Tego typu zadań oczywiście można wymyślić o wiele więcej. Wszystkie z nich możemy zrealizować przy pomocy Gulpa, a wywoływać przy pomocy prostych komend w linii poleceń, używając komendy gulp [task].

Przygotowanie środowiska (skrót dla początkujących)

Z założenia artykuł został napisany dla osób, które przynajmniej jakkolwiek orientują się czym jest Node.js, NPM oraz Gulp. Mimo pierwotnym założeniom, postanowiłem nieco uchylić czoła dla nieco mniej wtajemniczonych czytelników, dlatego też pozwoliłem sobie wtrącić krótki opis przygotowania środowiska, ograniczając się do bardzo ogólnych kroków:

  1. Do pracy z Gulp, będzie nam potrzebne środowisko Node.js - pozwala nam ono uruchamiać JavaScript po stronie serwera (choć w naszym przypadku ograniczymy się tylko do "budowania" szablonu z plików źródłowych). Pobieramy ze strony projektu odpowiednią dystrybucję środowiska, a następnie instalujemy ją. NPM (Node Package Manager) do pobierania wymaganych zależności i bibliotek otrzymamy tutaj w pakiecie.
  2. Sam Gulp instalujemy poprzez NPM, uruchamiając odpowiednie polecenia:
    • gulp install -g - instalujemy Gulp globalnie na naszym komputerze (tym sposobem będziemy mieć do niego dostęp z linii poleceń), operację można wykonać na dowolnym katalogu
    • gulp install - instalujemy Gulp w katalogu naszego szablonu, gdzie znajdzie się także plik Gulpfile.js
    • npm install [nazwa_paczki] - w ten sposób będziemy instalować wszystkie potrzebne pluginy do Gulp, z których będziemy korzystać w naszych taskach

Trzy, dwa, jeden, start!

Na początku naszych rozważań nad przykładowym użyciem Gulpa, przygotujmy zgrubną strukturę katalogów naszego projektu. Dla przejrzystości działań, dobrym zabiegiem będzie trzymanie kodu deweloperskiego i wersji produkcyjnej szablonu w osobnych folderach by móc spokojnie pracować na plikach, które dopiero za chwilę zostaną przygotowane i wtłoczone do wersji produkcyjnej.

  • /dist
  • /src
  • Gulpfile.js

Do katalogu src trafią wszystkie pliki, które będziemy wykorzystywać podczas pracy z szablonem - widoki szablonu w PHP, skrypty JavaScript, pliki SASS (czy ktoś używa jeszcze tradycyjnego CSS?) oraz wszelkiego rodzaju assety w postaci grafik. Osoby, które w jakiś sposób miały styczność z Gulp, powinny rozpoznać plik Gulpfile.js znajdujący się po za wskazanymi folderami, który będzie przechowywać nasze zadania uruchamiane przez nasz task runner.

Zadanie 1: Kopiowanie plików PHP do katalogu produkcyjnego

Jedną z najprostszych czynności jaką musi wykonać Gulp, jest kopiowanie plików PHP z katalogu src do katalogu dist. Tym zadaniem zajmiemy sie na początku i pokrótce zobaczymy jak wyglada task w Gulpfile.js:

var gulp = require('gulp');
 
gulp.task('php', function () {
    return gulp.src('src/.*php')
        .pipe(gulp.dest('./dist'));
});

Generalnie nie jest to zbyt skomplikowany kod - dołączamy Gulp do naszego skryptu, następnie rejestrujemy nowy task o nazwie "php", który wyszukuje wszystkie pliki z rozszerzeniem .php i kopiuje do katalogu dist. Po zapisaniu powyższego kodu w pliku Gulpfile.js, możemy uruchomić task poleceniem gulp php. (podobnie uruchamiać możemy wszystkie inne taski, które za chwilę zarejestrujemy).

Zadanie 2: Generowanie pliku style.css

W dzisiejszych czasach nie wyobrażam sobie pracy z plikami CSS bez prekompilatorow jak SASS (SCSS) czy LESS. Osobiście preferuje ten pierwszy, dlatego na jego przykładzie przygotowałem taśm, który bedzie kpmpilowac pliki .scss do ostatecznego, zminifikowanego pliku style.css, z którego bedzie korzystać szablon:

var gulp = require('gulp');
var sass = require('gulp-sass');

gulp.task('sass', function () {
    return gulp.src('src/sass/main.scss')
        .pipe(sass({outputStyle: 'compressed'})
            .on('error', sass.logError))
        .pipe(rename('style.css'))
        .pipe(gulp.dest('./dist'));
});

W tym przypadku kod jest nieco bardziej skomplikowany. Tak jak poprzednio - dołączamy potrzebne paczki node.js, rejestrujemy task. W samym zadaniu dla Gulp, task runner wykrywa plik main.scss, następnie kompiluje go do CSS (wszystkie pozostałe pliki SCSS muszą być dołączone wewnątrz pliku main.scss) w formie skompresowanej i zmienia jego nazwę na style.css, by ostatecznie skopiować do do katalogu z dystrybucją.

Nic oczywiście nie stoi na przeszkodzie by SCSS zastąpić np. LESS. Wystarczy zainstalować odpowiednią wtyczkę do Gulp i podstawić do powyższego przykładu zgodnie z jej dokumentacją.

Zadanie 3: Generowanie nagłówka style.css

Tworząc szablon dla WordPressa, chcielibyśmy, by ten mógł pobrać informacje o nim (np. numer wersji, autor itp.) i wyświetlić je przy liście dostępnych szablonów w panelu administracyjnym. Do tego potrzebny będzie nagłówek pliku style.css, w którym zamieszcza się tego typu dane. Jako, że w poprzednim kroku skompresowaliśmy nasz plik style.css, to nie zawiera on żadnych komentarzy (właśnie dlatego dopiero teraz zajmujemy się tworzeniem nagłówka tego pliku).

Idealnym rozwiązaniem byłby oczywiście task, który pobierałby wszystkie wymagane dane z pliku konfiguracyjnego header.json (moglibyśmy go trzymać po za katalogiem src), w którym bardzo łatwo moglibyśmy przechowywać wszystkie potrzebne informacje i bez trudu je modyfikować (przykład  wartości wyciągnięty z dokumentacji WordPress):

{
    "Theme Name": "Twenty Thirteen",
    "Theme URI": "http://wordpress.org/themes/twentythirteen",
    "Author": "the WordPress team",
    "Author URI": "http://wordpress.org/",
    "Description": "The 2013 theme for WordPress...",
    "Version": "1.0",
    "Licence": "GNU General Public License v2 or later",
    "Licence URI": "http://www.gnu.org/licenses/gpl-2.0.html",
    "Tags": [
        "black",
        "brown",
        "orange",
        "tan",
        "white"
    ],
    "Text Domain": "twentythirteen"
}

By móc przeprowadzić tę operację, będziemy musieli zainstalować dodatkowe pluginy do Gulp i nieco pokombinować, jednak dla nas programistów to chleb powszedni.

var gulp = require('gulp'),
    insert = require("gulp-insert"),
    header = require('./header.json');
 
gulp.task('css-header', ['sass'], function () {

    var header = "";

    for(var key in header){
        header += "\t" + key + ": " + header[key] + "\n";
    }

    return gulp.src('./dist/style.css')
        .pipe(insert.prepend('/*\n' + header + '\n*/\n'))
        .pipe(gulp.dest('./dist'));
});

Pokrótkie wyjaśnienie kodu: Dołączamy wymagane biblioteki oraz plik header.json, do którego mamy dostęp poprzez zmienną header. Rejestrujemy task css-header (ważne jest by zadanie to uruchamiać gdy jesteśmy pewnym, że plik ze stylami istnieje, dlatego w definicji podaliśmy tablicę z taskami, które muszą zostać wykonane przez stworzeniem nagłówka), w którym budujemy w pętli nagłówek spliku style.css, a następnie przy pomocy gulp-insert dołączamy go do tego pliku.

Zadanie 4: Minifikowanie plików JavaScript

Po przygotowaniu CSS pora na doprawienie szablonu odrobiną skryptów. Jak wiadomo, każdy request wykonany podczas wczytywania strony jest na wagę złota - dlatego też postanowiłem zebrać wszystkie pliki JavaScript do jednego miejsca.

var gulp = require('gulp'), 
    uglify = require("gulp-uglify"),
    concat = require("gulp-concat");
 
gulp.task('js', function () {
    return gulp.src('src/js/*')
        .pipe(concat('scripts.js'))
        .pipe(uglify())
        .pipe(gulp.dest('./dist'));
});

Zadanie Gulp także nie jest zbyt skomplikowane - najpierw Gulp wykrywa wszystkie pliki w katalogu src/js, następnie przy pomocy gulp-concat łączy je do jednego pliku scripts.js, by w końcu móc go zminifikować dzięki gulp-uglify i skopiować do katalogu dist. Ostatecznie ze wszystkich skryptów, które przygotowaliśmy dla naszego szablonu, wyprodukowaliśmy jeden, skompresowany plik, który w łatwy sposób możemy dołączyć do strony w pliku functions.php.

Zadanie 5: Kopiowanie assetów do katalogu produkcyjnego

Równie prostym zadaniem co w przypadku CSS, okazuje się być kopiowanie assetow:

var gulp = require('gulp'); 
 
gulp.task('assets', function () {
    return gulp.src('src/assets/**')
        .pipe(gulp.dest('./dist/assets/'));
});

Oczywiście cala operacja może być o wiele bardziej rozbudowana np. w przypadku gdy chcemy odpowiednie typy plików (obrazy, video etc.) rozdzielić do konkretnych katalogów.

Zadanie 6: Czyszczenie katalogu produkcyjnego

Przed każdorazowym tworzeniem dystrybucji, powinniśmy wyczyścić katalog dist, by pozbyć sie starych wersji plików:

var gulp = require('gulp');
var clean = require("gulp-clean");
 
gulp.task('clean', function () {
    return gulp.src('./dist', {force: true})
        .pipe(clean());
});

Powyższy przykład jest stosunkowo prosty - Gulp jedynie usuwa katalog dist wraz z cala jego zawartością.

Zadanie 7: Automatyczny deployment

Wiele osób korzysta z lokalnych serwerów PHP i pracuje nad szablonami na własnym komputerze. Jako ze nieczęsto działam z tym językiem, nie posiadam serwera lokalnego, a development szablonów WordPress wspieram hostingiem. W takiej sytuacji częstotliwość z jaka należy korzystać z FTP jest tak duża ze zaczyna być to uciążliwe. Postanowiłem wiec przygotować prosty task do automatycznego deploymentu szablonu na serwerze.

Przygotowałem prosty plik konfiguracyjny ftp.json dla naszego ftp:

{
    "host" : "Adres serwera FTP",
    "user" : "Użytkownik FTP",
    "password" : "Hasło do FTP",
    "path" : "Ścieżka do naszego szablonu na serwerze"
}

Mając wszystkie potrzebne dane w powyższym pliku, możemy przystąpić do stworzenia taska dla Gulp. W tym celu potrzebować będziemy paczki vinyl-ftp (do obsługi FTP) oraz gulp-util, który pomoże nam w wyświetlaniu logów wysyłki FTP.

var gulp = require('gulp'),
    ftp = require('vinyl-ftp'),
    config = require('./ftp.json'),
    gutil = require('gulp-util');

gulp.task('deploy', function () {

    var conn = ftp.create({
        host: config.host,
        user: config.user,
        password: config.password,
        parallel: 10,
        log: gutil.log
    });

    return gulp.src("./dist/**", {
            base: './dist', 
            buffer: false
        })
        .pipe(conn.newer(config.path))
        .pipe(conn.dest(config.path));

});

Na samej górze kodu dołączamy wymagane paczki oraz plik ftp.json. W tasku "deploy" na początku ustawiamy połączenie do naszego serwera FTP przy użyciu vinyl-ftp. Pole parallel określa liczbę żądań do FTP, które mogą być wykonywane jednocześnie, natomiast poprzez przekazanie gutil.log do paramteru log określamy sposób w jaki wyświetlane będą komunikaty dotyczące wysyłki na serwer. Następnie gulp wyszukuje wszystkie pliki w katalogu dist, sprawdza, któe z nich są nowsze niż te na serwerze i wysyła tylko te, które się zmieniły. Wszystko to mamy pod jednym prostym poleceniem - gulp deploy.

Zadanie 8: Spinamy wszystkie taski w jedno polecenie - gulp build

Do tej pory wszystkie zadania, które miał wykonywać Gulp prezentowałem osobno. Ostatecznie chcielibyśmy jednak, by móc to wszystko spiąć w całość i nie musieć ręcznie wykonywać każdego taska z linii poleceń. Zamiast tego, przygotujemy dodatkowy task - gulp build, który zbierze wszystko do kupy oraz ustalimy zależności pomiędzy taskami (przypominam - są one przechowywane jako tablice z nazwami tasków do wykonania "przed"):

var gulp = require('gulp'),
    sass = require('gulp-sass'),
    gutil = require('gulp-util'),
    ftp = require('vinyl-ftp'),
    rename = require("gulp-rename"),
    clean = require("gulp-clean"),
    uglify = require("gulp-uglify"),
    concat = require("gulp-concat"),
    insert = require("gulp-insert"),
    header = require('./header.json'),
    config = require('./project.json');

gulp.task('sass', ['clean'], function () {
    return gulp.src('src/sass/main.scss')
        .pipe(sass({outputStyle: 'compressed'})
            .on('error', sass.logError))
        .pipe(rename('style.css'))
        .pipe(gulp.dest('./dist'));
});

gulp.task('css-header', ['sass'], function () {

    var header = "";

    for(var key in header) {
        header += "\t" + key + ": " + header[key] + "\n";
    }

    return gulp.src('./dist/style.css')
        .pipe(insert.prepend('/*\n' + header + '\n*/\n'))
        .pipe(gulp.dest('./dist'));
});

gulp.task('assets', ['clean'], function () {
    return gulp.src('src/assets/**')
        .pipe(gulp.dest('./dist/assets/'));
});

gulp.task('js', ['clean'], function () {
    return gulp.src('src/js/*')
        .pipe(concat('scripts.js'))
        .pipe(uglify())
        .pipe(gulp.dest('./dist'));
});

gulp.task('php', ['clean'], function () {
    return gulp.src('src/.*php')
        .pipe(gulp.dest('./dist'));
});

gulp.task('clean', function () {
    return gulp.src('./dist', {force: true})
        .pipe(clean());
});

gulp.task('build', [
    'clean', 
    'variables', 
    'sass', 
    'php', 
    'assets', 
    'js'
]);

gulp.task('deploy', ['build'], function () {

    var conn = ftp.create({
        host: config.host,
        user: config.user,
        password: config.password,
        parallel: 10,
        log: gutil.log
    });

    return gulp.src("./dist/**", {
            base: './dist', 
            buffer: false
        })
        .pipe(conn.newer(config.path))
        .pipe(conn.dest(config.path));

});

Ostatecznie korzystać będziemy z dwóch poleceń - gulp build do budowania szablonu (jak widzicie, podane są przy nim tylko taski, które powinny być uruchomione podczas wywołania go) oraz gulp deploy, który wywoła proces budowania plików dystrybucyjnych i wyśle gotowy szablon z katalogu dist na wskazany przez nas serwer FTP.

Kilka słów na koniec

Zaprezentowana konfiguracja Gulp jest bardzo podobna do tej, z której korzystałem w ostatnim czasie przy nieoczekiwanym projekcie szablonu dla WordPress, więc mogę ręczyć, że jest to naprawdę niezwykle przydatne narzędzie. Podobnych zadań dla task runnera można wymyślić niezwykle dużo. Te, które tu pokazałem stanowią zaledwie niewielki odsetek tego co może wykonać Gulp. Sam nie jestem deweloperem WordPress, więc być może niektóre zastosowania i potrzeby przeoczyłem - dlatego też jestem ciekaw jakie zadania sami byście postawili przed Gulp i do czego chcielibyście go wykorzystać.

Zdaję sobie sprawę z tego, że artykuł niekoniecznie może być zrozumiały dla naprawdę początkujących, dlatego jeśli macie pytania, śmiało zadawajcie je w komentarzach. Razem z redakcją na pewno postaramy się na nie odpowiedzieć :)

DailyWeb poleca Kurs Wordpress dla początkujących

Zobacz