Gulp

Gulp

Gulp to kolejne znane w świecie frontendu narzędzie. Jego głównym celem jest automatyzacja wykonywania codziennych zadań. Jakich zadań? Wszystko zależy od nas. Od łączenia oddzielnych plików ze skryptami w jeden plik, minimalizacji skryptów, przetwarzania html, konwertowania scss na css, kopiowania plików po optymalizacja obrazków. W zasadzie możemy tutaj zautomatyzować dowolną czynność, którą wykonujemy podczas codziennej pracy frontend developera (poza piciem kawy, chociaż jakby poszukać w necie...).

Instalacja Gulpa

Instalacja Gulpa składa się z trzech kroków: instalacji konsoli Gulpa gulp-cli globalnie, dodaniu gulpa do danego projektu i stworzeniu konfiguracji dla gulpa w projekcie czyli pliku gulpfile.js.

Idąc zgodnie z instrukcją na stronie https://gulpjs.com/docs/en/getting-started/quick-start tworzymy w danym katalogu plik package.json komendą npm init -y, a następnie instalujemy odpowiednie pakiety:


npm i gulp-cli -g
npm i gulp -D

Po instalacji konsoli gulpa, będziemy mogli używać jego poleceń w terminalu. Wpisz gulp --help by zobaczyć odpowiednią listę.

Struktura projektu

Projekt, w którym będziemy działać będzie miał strukturę bardzo podobną do tej z poprzednich rozdziałów:


src
├── scss
│    └── main.scss
├── js
│    ├── app.js
│    └── other.js
├── images
│    ├── image1.jpg
│    └── image2.png
└── html
     ├── index.html
     └── other.html

dist
├── css
├── js
├── images
└── index.html

W naszej pracy wszystkie pliki źródłowe będziemy trzymać w katalogu src. Za pomocą gulpa będziemy je kompilować do katalogu dist w odpowiednie miejsca. W katalogu dist będzie więc nasza wynikowa strona.

Plik gulpfile.js

Gulp do swojego działania wymaga pliku gulpfile.js. Stwórz go w głównym katalogu projektu i dodaj przykładową konfigurację:


const gulp = require("gulp");

//nasze zadania
const nazwaZadania = function() {
    console.log("Tekst który pojawi się w konsoli");
});

Zadania w Gulpie są asynchroniczne. Jedno może trwać 1 sekundę, drugie 10 sekund, a ich operacje mogą wykonywać się w tym samym momencie.

Aby zasygnalizować zakończenie działania danego taska, zadanie musi zwrócić "stream", promise, "event emitter", "child process", albo "observable".

Co to oznacza? Nasze zadanie będą dzielić się na 2 typy:


//zadania które nie operują na plikach muszą zwracać funkcję zwrotną z parametru
const task1 = function(cb) {
    console.log("Jakiś tekst");
    cb();
}

//funkcje operujące na plikach zwracają stream kolejnych .pipe()
const task2 = function() {
    return gulp.src("src/scss/*.scss")
             .pipe(...)
             .pipe(...)
             .pipe(...)
             .pipe(gulp.dest("katalog_docelowy"))
}

Jeżeli zadanie nie będzie bezpośrednio operować na plikach za pomocą pipe(), będzie zwracać funkcje callback, którą podajemy w parametrze. Ten drugi typ zadań będą zwracać stream, który będzie składał się z nastu operacji pipe(). W naszym przypadku będzie to większość zadań.

Zadania operujące na plikach najczęściej będą pobierać pliki na których będą przeprowadzać operacje. Służy do tego funkcja gulp.src. Funkcja ta wymaga podania plików źródłowych, które mogą być przedstawione za pomocą jednego "wyrażenia", lub tablicy wyrażeń.


gulp.src("src/scss/style.scss"); //pobierz konkretny plik
gulp.src("src/scss/*.scss"); //pobierz wszystkie pliki .scss z tego katalogu
gulp.src("src/scss/**/*.scss"); //pobierz wszystkie pliki .scss z tego katalogu i podkatalogów
gulp.src("src/scss/*.+(scss|sass)"); //pobierz wszystkie pliki .scss i .sass z tego katalogu
gulp.src(["src/scss/style.scss", "src/scss/style2.scss"]); //pobierz 2 pliki
gulp.src(["src/js/**/*.js", "!src/js/test.js"]); //pobierz wszystkie pliki js oprócz pliku test.js
gulp.src(["*.js", "!temp*.js", "tempest.js"]); //pobierz wszystkie pliki js, oprócz tych zaczynających się na temp, ale pobierz tempest.js

Następnie za pomocą poleceń .pipe() będą wykonywać kolejne operacje na pobranych plikach. Pobieramy plik, wykonujemy na nim kilka operacji w pamięci, a następnie wynik zapisujemy za pomocą polecenia gulp.dest() w jakieś.

SCSS / CSS

Zacznijmy od jednej z najczęstszych rzeczy robionych za pomocą Gulpa, czyli zadania służącego do zamiany SCSS na CSS. Do tego celu wykorzystamy pakiet gulp-sass, który zainstalujmy w projekcie:


npm i gulp-sass -D

oraz użyjmy go w pierwszy zadaniu:


const gulp = require("gulp");
const sass = require("gulp-sass");


const css = function() {
    return gulp.src("src/scss/style.scss")
        .pipe(
            sass({
                outputStyle : "compressed" //styl kodu - extended, compressed
            }).on("error", sass.logError)
        )
        .pipe(gulp.dest("dist/css"));
}


exports.css = css;

Dart Sass

Gdy wejdziesz na stronę gulp-sass w sekcji basic usage zobaczysz linijkę:


...

sass.compiler = require('node-sass');

...

Oznacza ona kompilator który zostanie użyty do zamiany scss na css. W chwili pisania tego tekstu mamy 2 wersje sasa - czyli klasyczną używaną na razie przez większość ludzi - node-sass, oraz dart-sass. Ta druga jest nowszą wersją nad którą autor od jakiegoś czasu pracuje i zaleca jej używanie. Jeżeli chcesz używać nowej składni w sass, musisz z niej korzystać.

Autor SASS od jakiegoś czasu pracuje nad nową wersją swojego języka. Wprowadził moduły, uporządkował podział funkcji itp. Żeby zobaczyć o czym piszę, wejdź na chwilę na dokumentację, rzuciło ci się w oczy, że praktycznie każda funkcja jest zapisana w 2 wersjach - z kropką i bez kropki. By używać nowszej składni (z kropką) musisz zacząć używać dart-sass. W mojej opinii warto to zrobić już dzisiaj, bo jak widać na końcu powyższego artykułu, część funkcjonalności z klasycznego sass zostanie niedługo wycofana.


const gulp = require("gulp");
const sass = require("gulp-sass");

sass.compiler = require("sass"); //node-sass dla starszej wersji


const css = function() {
    return gulp.src("src/scss/style.scss")
        .pipe(
            sass({
                outputStyle : "compressed"
            }).on("error", sass.logError)
        )
        .pipe(gulp.dest("dist/css"));
}


exports.css = css;

Dodatkowo musimy do naszego projektu doinstalować moduł używający dart sass sass.


npm i sass -D

W tej chwili gdy w konsoli terminala odpalimy polecenie gulp css odpali się task który skompiluje plik src/scss/style.scss do dist/css/style.css.

Domyślne zadanie

Wyobraźmy sobie, że ściągamy jakiś projekt, instalujemy wszystko npm i, wpisujemy w terminalu gulp -T, i naszym oczom ukazuje się lista 30 tasków. Który task odpalić jako pierwszy?

Jeżeli w terminalu wpiszemy polecenie gulp nie podając nazwy taska, w terminalu wyskoczy nam błąd, że gulp próbował odpalić zadanie default, ale nie był w stanie go odnaleźć w naszym pliku.

Stwórzmy je:


const gulp = require("gulp");
const sass = require("gulp-sass");

sass.compiler = require("sass");


const css = function() {
    return gulp.src("src/scss/style.scss")
        .pipe(
            sass({
                outputStyle : "compressed"
            }).on("error", sass.logError)
        )
        .pipe(gulp.dest("dist/css"));
}


exports.default = gulp.series(css);
exports.css = css;

Od tej pory powinniśmy móc odpalić nasze zadania za pomocą polecenia gulp

W powyższym zadaniu pojawiła nam się nowa funkcja gulp.series(). Mamy w Gulpie dwie takie do użycia:

  • gulp.series(nazwaTaska1, nazwaTaska2, ...) - odpala kolejne zadania jeden po drugim, a na końcu zwraca wynik ich działania
  • gulp.parallel(nazwaTaska1, nazwaTaska2, ...) - odpala wszystkie zadania na raz nie czekając aż wcześniejsze się skończą

Sourcemaps

Nasza powyższa konfiguracja sprawia, że całe wynikowe style są spakowane w pojedyńczą linię (ponieważ dla sass użyliśmy outputStyle ustawionego na compressed).

Powoduje to, że gdy badamy dane elementy na stronie, debuger zawsze pokaże "pierwsza linia w style.css", przez co debugowanie staje się w zasadzie niemożliwe.

Żeby to naprawić, musimy skorzystać z pakietu sourcemaps:

npm i gulp-sourcemaps -D

const gulp = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");

sass.compiler = require("sass");


const css = function() {
    return gulp.src("src/scss/style.scss")
        .pipe(sourcemaps.init()) //odpalenie sourcemap przed zabawa na plikach
        .pipe(
            sass({
                outputStyle : "compressed"
            }).on("error", sass.logError)
        )
        .pipe(sourcemaps.write(".")) //po modyfikacjach na plikach zapisujemy w pamięci sourcemap
        .pipe(gulp.dest("dist/css"));
}


exports.default = gulp.series(css);
exports.css = css;

Normalnie sourcemaps rzutowanie kodu zapisuje w postaci komentarza w samym pliku css (na jego końcu). Jeżeli nam to przeszkadza i takie rzutowanie chcemy mieć w osobnym pliku, wskazujemy relatywne miejsce w pierwszym parametrze metody sourcemaps.write() - zwróć uwagę w powyższym kodzie na pojedynczą kropkę. Dzięki temu tuż obok style.css pojawi się plik style.css.map.

Autoprefixer

Do naszej kompilacji dodajmy 2 rzeczy. Po pierwsze nie chcemy myśleć o prefixach (-webkit-, -moz- itp). Nasz kod powinniśmy pisać ładnie bez śmiecenia, a odpowiednie prefixy powinny być dodawane w wynikowych css za naszymi plecami. Do automatyzacji dodawania prefixów służy narzędzie https://autoprefixer.github.io/.

Istnieje też wersja dla Gulpa. Wchodzimy na stronę https://www.npmjs.com/package/gulp-autoprefixer

i zgodnie z instrukcją instalujemy autoprefixera oraz dodajemy do naszej konfiguracji:


npm i gulp-autoprefixer -D

const gulp = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const autoprefixer = require("gulp-autoprefixer");

sass.compiler = require("sass");


const css = function() {
    return gulp.src("src/scss/style.scss")
        .pipe(sourcemaps.init())
        .pipe(
            sass({
                outputStyle : "compressed"
            }).on("error", sass.logError)
        )
        .pipe(autoprefixer()) //autoprefixy https://github.com/browserslist/browserslist#queries
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest("dist/css"));
}


exports.default = gulp.series(css);
exports.css = css;

Aby autoprefixer wiedział dla jakich przeglądarek musi dodać prefixy, musimy mu to jakoś powiedzieć. W poprzedniej wersji autoprefixera przeglądarki podawało się w parametrze browser tej paczki. W nowej wersji autoprefixer używa mechanizmu browserlist. Aby więc podać listę przeglądarek możemy postąpić tak jak opisane jest na stronie tego mechanizmu: https://github.com/browserslist/browserslist#readme.

Możemy więc stworzyć w głównym katalogu projektu plik .browserslistrc lub stosowną listę dodać do package.json naszego projektu (który już mamy).

Ja wybiorę ta drugą opcję. W pliku package.json dodaję wpis:


{
    ...
    "browserslist": [
        "defaults"
    ],
    ...
}

Watch - obserwowanie plików

Podczas pracy nad kodem ręczne odpalanie zadań po każdej zmianie w pliku było by bardzo niewygodne. Dlatego użyjemy watchera, który jest domyślnie dostępny w gulpie.

Po odpaleniu, watch działa sobie w tle obserwując zmiany na plikach. Gdy takie wykryje (czyli coś nich zmienimy i zapiszemy zmiany), odpali odpowiednie zadania, które wcześniej zdefiniowaliśmy, a które mu przekażemy w konfiguracji:


const gulp = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const autoprefixer = require("gulp-autoprefixer");

sass.compiler = require("sass");


const css = function() {
    return gulp.src("src/scss/style.scss")
        .pipe(sourcemaps.init())
        .pipe(
            sass({
                outputStyle : "compressed"
            }).on("error", sass.logError)
        )
        .pipe(autoprefixer())
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest("dist/css"));
}

const watch = function(cb) {
    gulp.watch("src/scss/**/*.scss", gulp.series(css));
    cb();
}


exports.default = gulp.series(css, watch);
exports.css = css;
exports.watch = watch;

Gdy teraz odpalimy naszą konfigurację poleceniem gulp, powinna się rozpocząć w terminalu początkowa kompilacja sass na css, a następnie gulp powinien nasłuchiwać zmian w plikach. Można to poznać po mrugającym kursorze w terminalu.

Żeby teraz przerwać działanie obserwowania, w terminalu naciśnij kilka razy Ctrl + C.

Zauważyłem, że przy dłuższej pracy obserwacja wielu plików zaczyna zwalniać. Kompilacja scss do css trwa coraz dłużej i dłużej. Nie tylko ja. Dodanie do watch kodu `{usePolling : true}` rozwiązuje sprawę. Pewnie w przyszłych wersjach zostanie to poprawione.


    gulp.watch("src/scss/**/*.scss", {usePolling : true}, gulp.series(css));
    

BrowserSync

BrowserSync umożliwia automatyczne odświeżanie strony po wykryciu zmian. Czyli my piszemy w edytorze i zwyczajnie olewamy odświeżanie przeglądarki po każdej zmianie w kodzie. Możemy więc na jednym ekranie mieć otwarty edytor, na drugim okno przeglądarki i automatycznie widzieć co się zmienia. Dodatkowo po uruchomieniu BrowserSync udostępnia on nam adres, na który możemy wejść dowolnym urządzeniem w danej sieci i synchronicznie przeglądać naszą stronę. Synchronicznie czyli jeżeli na danym urządzeniu ciut przewinę stronę, przewinie się ona na wszystkich urządzeniach. Jeżeli rozwinę menu, kliknę w link itp - zrobię to na wszystkich urządzeniach równocześnie.

Aby skorzystać z tych cudów musimy przejść na stronę https://www.browsersync.io/docs/gulp i zainstalować BrowserSync poleceniem:


npm i browser-sync -D

BrowserSync działa w "2 trybach". Jednym z nich jest automatyczne przeładowanie strony (reload). Drugim trybem jest wstrzykiwanie na stronę zmienionych styli bez przeładowywania strony (bardzo pomocne, gdy pracujemy np. nad połączeniami asynchronicznymi). To wstrzykiwanie wykonujemy za pomocą stream.

Aby BrowserSync mógł w ogóle działać, musimy dopisać task, który nam uruchomi server BrowserSync, oraz dodać dwie linijki do naszej konfiguracji.


const gulp = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const autoprefixer = require("gulp-autoprefixer");
const browserSync = require("browser-sync").create();

sass.compiler = require("sass");


const server = function(cb) {
    browserSync.init({
        server: {
            baseDir: "./dist"
        },
        notify: false, //reszta opcji z dokumentacji browsersync
        //host: "192.168.0.24",
        //port: 3000,
        open: true,
        //browser: "google chrome" //https://stackoverflow.com/questions/24686585/gulp-browser-sync-open-chrome-only
    });

    cb();
}

const css = function() {
    ...
}

const watch = function(cb) {
    ...
}


exports.default = gulp.series(css, server, watch);
exports.css = css;
exports.default = default;
exports.watch = watch;

Domyślne na serwerach pierwszym plikiem odpalanym po wejściu na stronę jest index.html. W powyższej konfiguracji wskazujemy, że serwer powinien startować w katalogu dist, dlatego powinien tam się taki plik znaleźć i to właśnie on będzie domyślnie otwierany przez BrowserSync.

Po odpaleniu gulpa (gulp), w terminalu zostaną udostępnione 4 adresy, a nasza strona powinna zostać odpalona w przeglądarce.

Gulp Browser sync

Pierwszy z tych adresów to adres na który powinieneś otworzyć (albo przeglądarka sama sobie go otworzyła). Drugi to adres na urządzeń zewnętrznych. Dwa kolejne to UI służące do konfiguracji zachowania się BrowserSync. Można tam wyłączyć symulację klikania, przewijania itp.

Powyżej odpaliliśmy serwer BrowserSync. Żeby teraz aktualizować html i css musimy do naszych zadań dodać odświeżanie:


const gulp = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const autoprefixer = require("gulp-autoprefixer");
const browserSync = require("browser-sync").create();

sass.compiler = require("sass");


const server = (cb) => {
    ...
}

const css = function() {
    return gulp.src("src/scss/style.scss")
        .pipe(sourcemaps.init())
        .pipe(
            sass({
                outputStyle : "compressed"
            }).on("error", sass.logError))
        )
        .pipe(autoprefixer())
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest("dist/css")) //tu nie ma średnika!
        .pipe(browserSync.stream());
}

const watch = function(cb) {
    gulp.watch("src/scss/**/*.scss", gulp.series(css));
    gulp.watch("dist/**/*.html").on("change", browserSync.reload);
    cb();
}


exports.default = gulp.series(css, server, watch);
exports.css = css;
exports.default = default;
exports.watch = watch;

Po odpaleniu tych zadań w naszej przeglądarce zostanie załadowana strona z katalogu dist. Każda zmiana w plikach tej strony spowoduje odświeżenie strony w przeglądarce. Fajnie.

Javascript

Na chwilę obecną jednym z lepszych narzędzi do zamiany Javascript jest webpack. Narzędzie to bez problemu możemy połączyć z naszym Gulpem, dzięki czemu wszystko odpalimy jednym poleceniem (gulp).

W naszym przypadku webpacka chcemy użyć nie tylko do minimalizacji kodu, ale też jego transpilacji na ES5. Żeby to zrobić skorzystamy z babel. Musimy więc nie tylko zainstalować webpack i webpack-cli, ale też moduły babel, babel-preset-env i babel-core


npm install -D babel-loader @babel/core @babel/preset-env webpack webpack-cli

Po instalacji paczek dodajemy zadanie dla JS do naszego pliku konfiguracyjnego:


const gulp = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const autoprefixer = require("gulp-autoprefixer");
const browserSync = require("browser-sync").create();
const webpack = require("webpack");

sass.compiler = require("sass");


const server = function(cb) {
    ...
}

const css = function() {
    ...
}

const js = function(cb) { //https://github.com/webpack/docs/wiki/usage-with-gulp#normal-compilation
    return webpack(require("./webpack.config.js"), function(err, stats) {
        if (err) throw err;
        console.log(stats);
        browserSync.reload();
        cb();
    })
}

const watch = function(cb) {
    gulp.watch("src/scss/**/*.scss", gulp.series(css));
    gulp.watch("src/js/**/*.js", gulp.series(js));
    gulp.watch("dist/**/*.html").on("change", browserSync.reload);
    cb();
}


exports.default = gulp.series(css, js, server, watch);
exports.css = css;
exports.watch = watch;
exports.js = js;

Żeby powyższa konfiguracja zadziałała, musimy do naszego projektu dodać dodatkowy plik konfiguracji dla samego webpacka. Tworzymy więc obok pliku gulpfile.js plik webpack.config.js i dodajemy w nim podstawową konfigurację:


module.exports = {
    entry: './src/js/app.js',
    output: {
        path: ${__dirname}/dist/js,
        filename: 'bundle.min.js'
    },
    watch: false,
    mode: 'production',
    devtool: "source-map",
    module: {
        rules: [
            {
                test: /\.m?js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            }
        ]
    }
}

HTML

Jeżeli chcemy ułatwić sobie pisanie kodu html, możemy pokusić się o dzielenie go na części. HTML będziemy pisać w katalogu src/html, a odpowiedni mechanizm będzie go scalał do katalogu dist. Tym mechanizmem będzie gulp-file-include. Wchodzimy więc klasycznie na jego stronę i instalujemy w naszym katalogu poleceniem:


npm i gulp-file-include -D

Następnie dodajemy ją do naszej konfiguracji oraz tworzymy stosowny task. Przy okazji dla watch zmieniamy dla HTML nasłuchiwany katalog na src/html:


const gulp = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const autoprefixer = require("gulp-autoprefixer");
const browserSync = require("browser-sync").create();
const webpack = require("webpack");
const fileInclude = require("gulp-file-include");

sass.compiler = require("sass");


const server = function(cb) {
    ...
}

const css = function() {
    ...
}

const js = function(cb) {
    ...
}

const html = function(cb) {
    return gulp.src('src/html/index.html')
        .pipe(fileInclude({
            prefix: '@@',
            basepath: '@file'
        }))
        .pipe(gulp.dest('dist'))
}

const htmlReload = function(cb) {
    browserSync.reload();
    cb();
}

const watch = function(cb) {
    gulp.watch("src/scss/**/*.scss", gulp.series(css));
    gulp.watch("src/html/**/*.html", gulp.series(html, htmlReload));
    gulp.watch("src/js/**/*.js", gulp.series(js));
    cb();
}


exports.default = gulp.series(css, js, html, server, watch);
exports.css = css;
exports.watch = watch;
exports.js = js;
exports.html = html;

Po odpaleniu tego plugina będziemy mogli w HTML używać zapisu


<html>
<body>
    <div>
    Dołączam pliki używając relatywnych ścieżek czyli z katalogu src/inc:
    @@include('./inc/header.html')
    @@include('./inc/main.html')
    @@include('./inc/footer.html')
    </div>
</body>
</html>

aby dołączać pliki do naszego html. Zasada taka sama jak funkcja include w PHP.

Dodatki: rename, csso, wait

Trzy mini dodatki, które można dodać - ale niekoniecznie. Moduł gulp-rename służy do zmiany nazwy wynikowego pliku. W naszym przypadku do tej pory generowany był plik style.css (ponieważ generujemy go na bazie pliku style.scss). Jeżeli chcemy inną nazwę pliku css - np. style.min.css, użyjemy do tego paczki gulp-rename.

Instalujemy tą paczkę i dodajemy do gulpa:


npm i gulp-rename -D

const gulp = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const autoprefixer = require("gulp-autoprefixer");
const browserSync = require("browser-sync").create();
const webpack = require("webpack");
const fileInclude = require("gulp-file-include");
const rename = require("gulp-rename");

sass.compiler = require("sass");


const server = function(cb) {
    ...
}

const css = function() {
    return gulp.src("src/scss/style.scss")
        .pipe(sourcemaps.init())
        .pipe(
            sass({
                outputStyle : "compressed"
            }).on("error", sass.logError))
        )
        .pipe(autoprefixer())
        .pipe(rename({ //zamieniam wynikowy plik na style.min.css
            suffix: ".min",
            basename: "style"
        }))
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest("dist/css")) //tu nie ma średnika!
        .pipe(browserSync.stream());
}

...

Drugi dodatek to https://github.com/css/csso. Moduł csso służy do większej optymalizacji styli. My na razie minimalizujemy nasze style do 1 linii. Ale czy dodatkowo optymalizujemy? Raczej nie. Dodatek ten np. połączy właściwości background-image, background-color w jednego shorthanda background itp. Ogólnie robi całkiem fajną robotę jeżeli chodzi o optymalizację css. Nas to nic nie kosztuje, więc dodajmy dodatkową rzecz, która przeleci przez nasz wynikowy plik i trochę go uporządkuje.

Wchodzimy więc na stronę https://github.com/css/csso i instalujemy ten moduł poleceniem:


npm i gulp-csso -D

a następnie dodajemy go do naszego zadania:


const gulp = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const autoprefixer = require("gulp-autoprefixer");
const browserSync = require("browser-sync").create();
const webpack = require("webpack");
const fileInclude = require("gulp-file-include");
const rename = require("gulp-rename");
const csso = require("gulp-csso");

sass.compiler = require("sass");


const server = function(cb) {
    ...
}

const css = function() {
    return gulp.src("src/scss/style.scss")
        .pipe(sourcemaps.init())
        .pipe(
            sass({
                outputStyle : "compressed"
            }).on("error", sass.logError))
        )
        .pipe(autoprefixer())
        .pipe(rename({ //zamieniam wynikowy plik na style.min.css
            suffix: ".min",
            basename: "style"
        }))
        .pipe(csso())
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest("dist/css")) //tu nie ma średnika!
        .pipe(browserSync.stream());
}

...

Trzeci dodatek tyczy się niektórych systemów. Podczas pracy może się zdarzyć, że w terminalu pojawi się dziwny komunikat, że node-sass nie mógł uzyskać dostępu do pliku. Ja tak miałem po n-tej aktualizacji Windows 10, ale widziałem u znajomych na innych systemach podobny problem. W tym przypadku pomaga zainstalowanie modułu gulp-wait i odpalenie go na początku zadania z SCSS:


const gulp = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const autoprefixer = require("gulp-autoprefixer");
const browserSync = require("browser-sync").create();
const webpack = require("webpack");
const fileInclude = require("gulp-file-include");
const rename = require("gulp-rename");
const csso = require("gulp-csso");
const wait = require("gulp-wait");

sass.compiler = require("sass");


const server = function(cb) {
    ...
}

const css = function() {
    return gulp.src("src/scss/style.scss")
        .pipe(wait(500))
        .pipe(sourcemaps.init())
        .pipe(
            sass({
                outputStyle : "compressed"
            }).on("error", sass.logError))
        )
        .pipe(autoprefixer())
        .pipe(rename({ //zamieniam wynikowy plik na style.min.css
            suffix: ".min",
            basename: "style"
        }))
        .pipe(csso())
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest("dist/css")) //tu nie ma średnika!
        .pipe(browserSync.stream());
}

...

Podobnych dodatków jest wiele. Większość z nich znajdziesz na stronie https://github.com/alferov/awesome-gulp.

Gotowa konfiguracja

Konfigurację z powyższego tekstu znajdziesz tutaj.

Wszelkie prawa zastrzeżone. Jeżeli chcesz używać jakiejś części tego kursu, skontaktuj się z autorem.
Aha - i ta strona korzysta z ciasteczek.