Ловимо помилки правильно
У всіх сучасних системах складання фронтенда є режим watch, при якому запускається спеціальний демон для автоматичного перескладання файлів відразу після їх збереження. Також він є gulp.js, але з деякими особливостями, що роблять роботу з ним трохи складніше. Робота gulp.js ґрунтується на обробці файлів як потоків даних (streams). І помилки в потоках не завжди можна перехопити, а коли помилка трапляється, то буде викинуто неперехоплене виняток і завершиться процес.
Щоб цього не відбувалося, і watcher міг ігнорувати окремі помилки і запускатися знову і знову при збереженні файлів, в gulp потрібно зробити кілька додаткових налаштувань, про які розповість у цій статті.
Що відбувається?
Для початку розберемося, що відбувається і чому ж watcher іноді падає. При написанні плагіна до gulp рекомендується випускати подію error у разі виникнення помилок під час роботи плагіна. Але nodejs-потоки, на яких заснована система складання, не дозволяють помилкам залишатися непоміченими. У випадку, якщо на подію error ніхто не підписався, викинеться виняток, щоб повідомлення точно досягло користувача. В результаті, при роботі з gulp розробники часто бачать такі помилки
При застосуванні Continious-Integration (CI), коли на кожен коміт запускаються автоматичні перевірки, це може бути корисним (складання провалилося, білд не зібрався, відповідальні отримають листи). Але вічно падаючий watcher у локальній розробці, який потрібно постійно перезапускати – це велика неприємність.
Що робити?
В інтернеті можна знайти питання цій темі як на stackoverflow, так і на тостері. У відповідях запитання пропонують кілька популярних рішень.
Можна підключитиплагін gulp-plumber, який не тільки підпишеться на error для одного плагіну, але й автоматично зробить це і для всіх наступних плагінів, підключених через pipe:
Чому так не треба робити
Незважаючи на те, що проблема виглядає вирішена, це не так. Тепер помилки складання перехоплюються, ми отримуємо в консоль повідомлення про це, запущений демон-watcher не падає, але ми тепер маємо нову проблему.
Припустимо, у нас є CI-сервер, де йде складання наших скриптів для викладки. Щоб наша збірка працювала разом з watch, ми застосували одне з рішень вище. Але тепер виявляється, що при помилках у збиранні наш білд все одно відзначається як успішний. Команда gulp завжди завершується з кодом 0. Виходить, для CI-складання нам не потрібно ковтати помилки. Можна додавати свій обробник помилок тільки для watch режиму, але це ускладнить опис складання і підвищить шанси припуститися помилки. На щастя, є рішення, як налаштувати складання однаково, але при цьому підтримати роботу і в режимі білда і в режимі watch.
Насправді gulp намагається слухати помилки у потоках файлів. Але через те, що зазвичай останнім йде виклик gulp.dest() , що записує результат на диск, а помилки трапляються в проміжних плагінах, то помилка не досягає кінця ланцюжка, тому gulp про неї не дізнається. Наприклад розглянемо такий ланцюжок:
На відміну від, наприклад, promise, помилки в потоках не поширюються далі ланцюжком, їх потрібно перехоплювати в кожному потоці окремо. З цього приводу є pull-request у io.js, але в нинішніх версіях передати помилку в кінець ланцюжка не вийде. Тому gulp не може перехопити помилки проміжних потоків і нам це потрібно робити самостійно.
Зате gulp як опис task приймає не тільки функцію, що повертає потік, а йзвичайну callback-style функцію, як і багато API в node.js. У такій функції ми самі вирішуватимемо, коли завдання завершилося з помилкою, а коли успішно:
Такий опис складання виглядає трохи більше, тому що ми тепер робимо частину роботи за gulp, але тепер ми її робимо правильно. А якщо написати собі допоміжну функцію, подібну до такої, то різниця буде практично непомітна:
Натомість ми отримаємо правильні повідомлення в консолі як під час розробки та перескладання при збереженні, так і на CI-сервері під час білду.
Хардкорна конфа за С++. Ми запрошуємо лише профі.