Истории отладки: как скетч сломал CKEditor
16 февраля мне посчастливилось столкнуться с одной из самых странных ошибок, с которыми я когда-либо сталкивался. Давайте установим сцену:
(Это столкновение с кодером, и некоторые детали были изменены, чтобы защитить личность подопечного)
В один из субботних дней я получил сообщение от кодеров о проблеме с вебпаком. У нашего клиента, которого мы будем называть Джорджем, возникли проблемы с тем, как заменить значки в CKEditor некоторым выражением регулярного выражения. Немедленно, NormalModuleReplacementPlugin
приходит на ум. API прост и позволяет вам заменить любой модуль, вызываемый require
или импортировать с любым другим произвольным модулем.
Я отвечаю Джорджу и говорю, что могу помочь ему с этой проблемой. Это кажется достаточно простым и не должно занимать слишком много времени.
У Джорджа есть несколько файлов, которые он хочет переопределить с помощью веб-пакета, поэтому мы решили собрать некоторые вспомогательные функции внутри веб-пакета.
Во-первых, мы определили некоторый четкий и последовательный API, который позволил бы нам настроить этот помощник. В итоге мы получили массив объектов конфигурации следующим образом
const replaceModules = [
{ match: /some-icon\.svg/, replace: path.resolve(__dirname, 'src/icons/new-icon.svg') }
]
Большой! это позволило бы нам определить функцию, которая генерирует плагины для обработки каждого случая.
function createModuleReplacements(configurations) {
return configurations.map( config =>
new webpack.NormalModuleReplacementPlugin(config.match, config.replace)
)
}
Затем мы интегрируем это в плагины веб-пакета, распространяя их в конфигурации плагинов. Все хорошо и хорошо.
Мы протестировали его с одним элементом конфигурации, и он работал плавно. Проблемы начались, когда мы добавили несколько элементов конфигурации. Внезапно иконки начали дублироваться. Каждый раз, когда мы добавляли новую конфигурацию, один и тот же значок начинал появляться в одном и том же месте.
Большой. Некоторая недетерминированная, связанная с порядком ошибка веб-пакета и разрешения модуля. Вероятно, это одни из самых сложных ошибок для отслеживания. Итак, мы решили выяснить, что происходит.
Идея 1. Возможно, у веб-пакета проблемы с обработкой всех этих копий плагина. Это натянуто, но что, если бы мы использовали один плагин, который принимает функцию, вместо того, чтобы распространять несколько плагинов.
new webpack.NormalModuleReplacementPlugin(/.+\.svg$/, resource => {
// we first check if this resource matches one of the items in our config
const configuration = replaceModules
.find( configuration => configuration.match.test( resource.request ) )
if (configuration) {
resource.request = configuration.replace // re asign the configuration
}
})
Итак, мы получаем это и даем ему сборку. Нет, та же проблема.
Идея 2. Может быть, мы совпадаем сами с собой. У нас было несколько сменных модулей, которые были подмножествами других запросов регулярных выражений. Может быть, мы перезаписывали себя
(мы даем этому шанс)
До сих пор нет кости.
На данный момент я клонировал исходный код библиотеки, начал воссоздавать среду сборки и копаться в node_modules.
Тут я замечаю нечто странное. Вот так выглядит иконка в библиотеке
<svg viewBox="0 0 20 20" xmlns=" d="M10... (truncated)"/></svg>
А вот так выглядели svg, которые были у нас в исходниках
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns=" xmlns:xlink="
<!-- Generator: Sketch 52.2 (67145) - -->
<title>👾 (...)</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M5... (truncated)" id="path-1"></path>
</defs>
<g id="🌈(...)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Bounds" x="0" y="0" width="24" height="24"></rect>
<rect id="Live-Area" x="2" y="2" width="20" height="20"></rect>
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Text-Align-Right" fill="#293F4D" fill-rule="nonzero" transform="translate(11.842090, 11.752865) scale(-1, 1) translate(-11.842090, -11.752865) " xlink:href="#path-1"></use>
<g id="🌈-(...)" mask="url(#mask-2)" fill="#293F4D">
<rect x="0" y="0" width="24" height="24"></rect>
</g>
</g>
</svg>
Эскиз. экспорт. смайлики. в SVGS.
Хотя смайлики определенно не были проблемой, эта мысль все еще беспокоила меня.
Обычно это не было бы проблемой, если бы мы использовали какой-либо загрузчик файлов или другой механизм для объединения этих ресурсов вместе. К сожалению, мы использовали raw-loader
чтобы включить эти активы в окончательную сборку js. Это означало, что эти файлы вставлялись байт за байтом в наш пакет js.
Я не совсем уверен, ПОЧЕМУ это сломало CKEditor. Все, что я знаю, это то, что удаление из этого файла всех дополнений исправило нашу сборку, и все отобразилось правильно.
Отличный способ провести 4 часа. 10/10 будет отлаживать снова.