Продолжим тему дебага историей из реальной жизни.
Это случилось на проекте с Ангуларом, но самого Ангулара вы не увидите, и все техники используемые тут могут быть применены на любом проекте.
Можете попробовать воспроизвести тут:
github.com/codelab-fun/co…
При запуске тестов на проекте (на котором тесты не запускались очень давно), выпадала странная ошибка:
> npm test feedback
:ERROR [config]: File ./libs/feedback/karma.conf.js does not exist!
При этом сам файл существовал, например cat выводил его содержимое.
Тут важно понимать, что (почти) все что, что запускается из npm, это обычный JavaScript, а значит код скорее всего лежит в папке node_modules.
Попробуем найти код отвечающий за это сообщение по части сообщения в папке node_modules
Нам повезло: результаты довольно однозначные, поэтому мы просто добавляем "debugger" перед выведением ошибки.
Когда мы запустим наше приложение в дебаг режиме, дебаггер остановится в этом месте и мы сможем разобраться - что именно тут происходит.
Пришло время взять в руки дебаггер! C node.js это немного посложнее чем с браузером, но ничего невозможного. Чтобы запустить node в дебаг-режиме, нам нужно добраться до .js файла, который запускается под капотом.
В package.json мы видим что "npm test" просто запускает "ng test"
Локальный бинарник ng лежит в папке node_modules/.bin, и это просто javascript файл.
Это значит что мы просто можем запустить:
node node_modules/.bin/ng test feedback
И получить тот же самый результат!
Добавим --inspect-brk, чтобы запустить nodejs в дебаг режиме:
$ node --inspect-brk node_modules/.bin/ng test feedback
При запуске nodejs остановится и подождет, пока мы присоединимся к нему дебаггером
Дебаггеры бывают разные, мы используем Chrome Dev Tools: в Chrome заходим по адресу: chrome://inspect/#devices
И видим снизу наш node процесс, нажав на который мы попадем наконец-то в дебаггер!
На самом деле есть альтернатива (Но еще не очень стабильная) всем этим сложным махинациям, можно использовать npmjs.com/package/ndb
npm i -g ndb
ndb npm test feedback
Так или иначе, мы уже в дебаггере. Теперь разберемся как он поможет нам починить баг.
На этой драматической ноте я пойду посплю, потому что болею, и у меня по ходу температура 😫
Когда я вернусь расскажу почему карма выдает такое странное сообщение и как это можно починить.
Ок, посмотрим что тут происходит:
Мы находимся файла config в karma.js
Функция в которой мы сейчас - parseConfig - получает конфиг для тестов
Карма пытается получить (require) fileConfigPath. По ходу будет выброшена ошибка, которая будет соответствовать двум критериям:
Тип ошибки - MODULE_NOT_FOUND
В сообщении ошибки - fileConfigPath - путь файла который мы пытаемся получить.
В образовательных целях давайте попробуем поймать эту ошибку.
Для этого нам нужно остановиться ДО require(...). Чтобы это сделать:
Поставим точку останова (breakpoint) на строке 351
Перезапустим скрипт из терминала
Убедимся что все гуд:
Мы Остановились на нужной строчке
до require()
Теперь:
3) Включим остановку при выбрасывании ошибки
4) Поставим галку, чтобы мы остановились даже если ошибка внутри try/catch
5) Поехали!
Вот наша первая остановка 🔥
Мы где-то внутри cjs (closure js) загрузщика модулей node.js. Глубоко!
Ошибка "Cannot find module '../../karma.conf", не совсем то, что мы ожидали
Код подходит под ожидание
Прежде чем мы посмотрим message, глянем в стэк вызовов:
Мы пришли сюда из кармы, parseConfig, как и ожидалось.
Также мы видим в стеке вызовов karma.conf.js, по ходу это и есть наш "Несуществующий файл"
Заглянем туда, нажав на соответствующий вызов
Вот и наш файл.
Во-первых он все-таки есть.
Во-вторых он пытается загрузить несуществующий файл.
Это вызывает ошибку, вроде все понятно.
Но почему же карма выводит неправильное имя 😫?
Нажмем продолжить (F8) и вернемся в место дебага все все началось:
Ок, вернувшись в начала мы:
Убедимся что это та же самая ошибка.
Вроде код и сообщение совпадают.
Единственно что не понятно, как получилось так, что e.message содержит в себе имя файла?
Давайте посмотрим на e.message
Да вот же он, прямо в стеке вызовов. По какой-то причине загрузчик модулей посчитал что добавить стек в сообщение это хорошая идея.
Давайте вернемся в тот код и посмотрим как это могло случиться...
Возвращаемся в код загрузчика и видим что стек добавляется в сообщение вручную.
резюме:
Карма ловит ошибку при загрузке конфига и делает две проверки:
code === MODULE_NOT_FOUND
✅ Верна для всех вложенных ненайденых модулей
message.includes(configFilePath)
✅Верна для всех вложенных ненайденых модулей, т.к. configFilePath в стеке вызовов
Что делать дальше?
В целом понятно как починить это локально - убрать сломанный require
Это 100% баг кармы, надо починить и там, чтобы сэкономить время другим людям. В тот момент я выбрал создать issue а не PR, т.к. не было времени. Вот оно: github.com/karma-runner/k…
Через пару недель кто-то сделал PR (github.com/karma-runner/k…).
Вместо добавления новых условий они просто убрали существующие и стали выводить непосредственно ошибку:
На этом детективная история со счастливым финалом закончена!
Спасибо всем кто прочитал до конца 🔥🔥🔥
У меня заняло примерно в 5 раз больше времени написать про этот баг чем починить :)