ReferenceError: a is not defined / Safari class fields implementation is buggy
Напомню, переделывая компиляцию в монорепозитории, мы взяли самый современный таргет компиляции — esnext.
Само собой, у нас есть пользователи, у которых браузеры старее. Особенно сафари, который обновляется только вместе с операционной системой.
В проектах, которые используют такие пакеты, мы просто транспайлим все нод-модули в скоупе
Задеплоив, мы увидели всплеск ошибок «a is not defined» в Rollbar’е в сафари с крайне непонятным стек-трейсом.
Ну, начали разбираться. Честно говоря, даже немного стыдно, т.к. проблема оказалось совсем нелепой, а для того, чтобы понять в чём дело понадобилось аж 3 разработчика…
Начали с поиска проблемного места.
Беда в том, что у нас регулярно возникают проблемы с сорсмапами в ролбаре — то они не заливаются, то не подхватываются в веб-интерфейсе. Из-за этого в ролбаре стектрейс частенько указывает на минифицированные файлы, в которых, как вы понимаете, бесполезно что-то искать: 3 строка и 58375 колонка, хех! А в дев-режиме проблема, естественно, не воспроизводилась.
Несмотря на кажущуюся бесмысленность, я всё таки попробовал поискать нужную колонку — никакой переменной «a» там и рядом не было.
Решили попробовать выкатить неминифицированный код, чтобы убедиться, что проблема именно в минифакторе, т.к. это самое большое отличие дев и прод режимов. Выкатили — проблема ушла. Обновляем terser (минифактор, который использует вебпак) — не помогает.
Решаем попытаться отловить проблему локально в прод режиме с включенными сорсмапами, и у одного из нас получается! Но при этом стектрейс абсолютно бесмысленный, указывает чёрт знает куда. А ещё проблема очень плавающая — появляется при первом визите без кеша и то не всегда. Загадка!
Ищем ишью в терсере, бабеле — ничего не находим. Настроение упадническое, т.к. пытаемся разобраться уже дня 4, пробуем переписывать какие-то странные места со смесью UMD, ESM и CommonJS — ничего не помогает.
Я набираюсь сил и отправляюсь на поиски злосчастной переменной «a». Прогоняю минифицированный файл через преттиер включаю в поиске Match Whole Word и нахожу 100+ переменных. Проверяю каждую из них: где объявляется и как используется. Смотрю раз, смотрю два, смотрю три и нахожу подозрительное место — незатранспайленный class field. Примерно в это же время мой напарник скидывает мне ишью Safari class fields implementation is buggy - ReferenceError: Can't find variable. Чудесно.
Проблема выглядит так:
Т.е. если в class field есть вызов функции, объявленной за пределами класса, то всё взрывается, а стектрейс указывает на абсолютно другое место.
Но почему так в принципе получилось? А всё просто — я забыл добавить ту самую транспиляцию нод-модулей со скоупом
Но ещё более стыдно за сам процесс дебага и сколько времени он занял (напомню, 4 дня).
Ретроспективно становится очевидно, что процесс дебага должен был быть другим:
1. Действительно имело смысл попробовать найти место в коде на основе строки и столбца, хоть этот пункт и не увенчался успехом.
2. Вместо деплоя неминифированного кода, нужно было выключить mangle. Эта опция отвечает за переименование переменных: yourVeryLongVariable → a — и тогда в ошибке мы бы увидели оригинальную переменную и сразу было бы понятно, на какой участок кода смотреть.
Вот так вот просто, да! А ещё мы почему-то не посмотрели на статистику браузеров в Ролбаре — тогда стало бы очевидно, что в 16-ом сафари проблему починили, и именно по этому только у одного из нас проблема иногда воспроизводилась — у него был 15-ый сафари.
В общем, ещё раз стыдно! Вроде бы опытные ребята, а так затупили! В оправдание остается сказать, что это была первая рабочая неделя после новогодних праздников — видимо мозги не включились ещё :D
Напомню, переделывая компиляцию в монорепозитории, мы взяли самый современный таргет компиляции — esnext.
Само собой, у нас есть пользователи, у которых браузеры старее. Особенно сафари, который обновляется только вместе с операционной системой.
В проектах, которые используют такие пакеты, мы просто транспайлим все нод-модули в скоупе
@aviasales
— это позволяет не париться на счёт обновления таргета тайпскрипта в монорепе и транспайлить через Babel только то, что нужно на основе текущего конфига Browserslist.Задеплоив, мы увидели всплеск ошибок «a is not defined» в Rollbar’е в сафари с крайне непонятным стек-трейсом.
Ну, начали разбираться. Честно говоря, даже немного стыдно, т.к. проблема оказалось совсем нелепой, а для того, чтобы понять в чём дело понадобилось аж 3 разработчика…
Начали с поиска проблемного места.
Беда в том, что у нас регулярно возникают проблемы с сорсмапами в ролбаре — то они не заливаются, то не подхватываются в веб-интерфейсе. Из-за этого в ролбаре стектрейс частенько указывает на минифицированные файлы, в которых, как вы понимаете, бесполезно что-то искать: 3 строка и 58375 колонка, хех! А в дев-режиме проблема, естественно, не воспроизводилась.
Несмотря на кажущуюся бесмысленность, я всё таки попробовал поискать нужную колонку — никакой переменной «a» там и рядом не было.
Решили попробовать выкатить неминифицированный код, чтобы убедиться, что проблема именно в минифакторе, т.к. это самое большое отличие дев и прод режимов. Выкатили — проблема ушла. Обновляем terser (минифактор, который использует вебпак) — не помогает.
Решаем попытаться отловить проблему локально в прод режиме с включенными сорсмапами, и у одного из нас получается! Но при этом стектрейс абсолютно бесмысленный, указывает чёрт знает куда. А ещё проблема очень плавающая — появляется при первом визите без кеша и то не всегда. Загадка!
Ищем ишью в терсере, бабеле — ничего не находим. Настроение упадническое, т.к. пытаемся разобраться уже дня 4, пробуем переписывать какие-то странные места со смесью UMD, ESM и CommonJS — ничего не помогает.
Я набираюсь сил и отправляюсь на поиски злосчастной переменной «a». Прогоняю минифицированный файл через преттиер включаю в поиске Match Whole Word и нахожу 100+ переменных. Проверяю каждую из них: где объявляется и как используется. Смотрю раз, смотрю два, смотрю три и нахожу подозрительное место — незатранспайленный class field. Примерно в это же время мой напарник скидывает мне ишью Safari class fields implementation is buggy - ReferenceError: Can't find variable. Чудесно.
Проблема выглядит так:
import helper from './helper'
class C {
property = helper(() => {})
}
Т.е. если в class field есть вызов функции, объявленной за пределами класса, то всё взрывается, а стектрейс указывает на абсолютно другое место.
Но почему так в принципе получилось? А всё просто — я забыл добавить ту самую транспиляцию нод-модулей со скоупом
@aviasales
… Стыдно!Но ещё более стыдно за сам процесс дебага и сколько времени он занял (напомню, 4 дня).
Ретроспективно становится очевидно, что процесс дебага должен был быть другим:
1. Действительно имело смысл попробовать найти место в коде на основе строки и столбца, хоть этот пункт и не увенчался успехом.
2. Вместо деплоя неминифированного кода, нужно было выключить mangle. Эта опция отвечает за переименование переменных: yourVeryLongVariable → a — и тогда в ошибке мы бы увидели оригинальную переменную и сразу было бы понятно, на какой участок кода смотреть.
Вот так вот просто, да! А ещё мы почему-то не посмотрели на статистику браузеров в Ролбаре — тогда стало бы очевидно, что в 16-ом сафари проблему починили, и именно по этому только у одного из нас проблема иногда воспроизводилась — у него был 15-ый сафари.
В общем, ещё раз стыдно! Вроде бы опытные ребята, а так затупили! В оправдание остается сказать, что это была первая рабочая неделя после новогодних праздников — видимо мозги не включились ещё :D