Этапы разработки браузерного расширения в Chrome на React: рассматриваем на примере Cloudhood
Мы решили сделать браузерное расширение на React, чтобы обеспечить стабильную среду для разработки и параллельно тестировать доработки. По ходу того, как писалось расширение, родился этот гайд — в нем рассказываю про опыт нашей команды, делюсь наработками и ссылками на GitHub.
Расширения позволяют делать быстрее повседневные или рабочие задачи. Например:
блокировать всплывающую рекламу — это делает знаменитый Adblock;
менять внешний вид HTML-страниц;
перехватывать и модифицировать заголовки и тела запросов и ответов;
создавать инструменты для работы с библиотеками и API в разработке;
переводить тексты и веб-страницы на другие языки;
отслеживать цены и скидки в интернет-магазинах;
улучшать доступность сайтов для пользователей с ограниченными возможностями.
Этим возможности расширений не ограничиваются, все зависит от API браузера и вашей фантазии.

Задача, которую мы хотели решить разработкой расширения
Нам расширение понадобилось, чтобы несколько команд могли параллельно работать над своими фичами. Например, разработчики пушат свои ветки и хотят их одновременно потестировать. Если доработки идут в разных репозиториях, на помощь придут микрофронтенды, которые можно тестировать параллельно, но это решит только часть задач.
Как у нас устроен CI/CD-процесс
Разработчик создает новую фичу.
Получившийся код пушится в GitLab.
В GitLab запускаются пайплайны, проходят проверки кода.
Приложение собирается и деплоится в Docker на dev-стенд.
Все готово, но возникает вопрос: как обеспечить параллельное тестирование разных версий определенного приложения на одном dev-стенде? Здесь помогает браузерное расширение Cloudhood, оно позволяет с помощью прокидывания хедеров в запросах подтягивать нужную версию приложения. В статье на Хабре рассказываем, как это у нас реализовано.
Конфигурация расширения Cloudhood
Прежде чем приступить к разработке, создадим стандартную структуру проекта. В нашем случае она выглядит так:


Для создания стандартной структуры нужно следующее:
1. Создать package.json.
2. Добавить README.md — markdown-файл, в который стоит внести детальное описание проекта.
3. Добавить конфиги eslint, Prettier и TypeScript.
4. Настроить husky и lint-staged для прекоммит-хуков, в которых будут прогоняться eslint и Prettier.
5. Добавить .gitignore — он указывает, какие файлы не добавлять в систему контроля версий. Это поможет исключить ненужные артефакты и не раздуть репозиторий.
6. Выбрать лицензию. В нашем случае подошла Apache License 2.0.
7. Подобрать структуру проекта, которая будет комфортна. Мы остановились на Feature-Sliced Design:

8. Создать index.html и index.tsx. как входную точку в приложение — аналогично обычному приложению на React.
9. Добавить файл manifest.json. Это файл конфигурации для расширений в браузере Google Chrome. Он содержит ключевые сведения о версии, иконках, разрешениях и визуальной части расширения. Файл пригодится в дальнейшем для того, чтобы опубликовать наше расширение.

Содержание manifest.json:
1. manifest_version — версия файла конфигурации. Рекомендую использовать v3, так как Chrome постепенно отказывается от v2.
2. icon и default_icon — иконки, которые будут отображаться в браузере у всплывающего окна расширения:

4. default_popup — HTML-файл или попап, который будет отображаться при клике на иконку расширения. Это визуальная часть расширения.
5. icons — набор иконок разных размеров, которые будут на страницах интернет-магазина Chrome. У каждой страницы свои размеры, допустим:

5. host_permissions — права доступа, они определяют, на каких сайтах будет работать расширение. В нашем случае расширение работает везде.
6. permissions — разрешения, или АPI Chrome, которые будут использоваться. Chrome детально проверяет пермишены ради безопасности пользователей — злоумышленники могут использовать расширения, чтобы воровать данные пользователей. Если не добавить разрешение, при установке приложения оно не будет работать. Также не стоит добавлять лишнего: нужно обосновать наличие каждого разрешения, и если добавить какое-то необоснованно, есть риск, что расширение не опубликуют.
7. background используется для работы сервис-воркера (Service Worker), который управляет фоновой работой расширения.
Готово, приложение сконфигурировано. Теперь можно писать компоненты.
Как использовать API Chrome
В этом разделе рассмотрим, как задействовать API Chrome для изменения запросов на сайте:
1. Задействуем storage.local, чтобы сохранить хедеры для запросов, когда удаляем, меняем или добавляем их. Можно сказать, что это аналог localStorage браузера.

2. Сообщаем сервис-воркеру, что нужно обновить заголовки с помощью chrome.runtime.sendMessage.

3. Сервис-воркер в файле background.ts слушает входящие сообщения (onMessage).

4. Если сообщение получено, сервис-воркер извлекает сохраненные заголовки из хранилища и применяет обновленные заголовки ко всем запросам браузера с помощью API chrome.declarativeNetRequest.updateDynamicRules.

Я рассказал о роли background.ts, теперь опишу процесс запуска и сборки приложения.
Запуск и сборка расширения
React-приложение можно запустить несколькими способами: локально или собрав и загрузив в браузер. Локальный запуск удобнее, поскольку hot-reload работает стабильнее и упрощает отладку.
Для этого в package.json добавляется скрипт start, как видно на скриншоте. Этот скрипт конфигурирует webpack-dev-server. Детали можно посмотреть в репозитории.

Для финальной сборки выполняем скрипт build, который использует конфигурацию из webpack.config.js.
Собираем две точки входа — index.tsx (интерфейс расширения) и background.ts для сервис-воркера — и помещаем их в папку build:

Копируем manifest.json в папку build с помощью плагина copy-webpack-plugin:

Таким же способом копируем картинки и index.html в build:

После этого проект полностью собран и готов к загрузке в браузер.
Как добавить расширение в Chrome
1. Открыть управление расширениями в браузере Chrome. Путь: три точки в правом верхнем углу вкладки → Расширения → Управление расширениями.
2. С помощью ползунка включить режим разработчика.

3. В разделе Расширения нажать Загрузить распакованное расширение, чтобы добавить приложение.

Сборка и релиз на GitHub
Автоматизируем сборку и релиз с помощью GitHub Actions — для этого надо настроить проверку лицензий, выполнение линтеров, сборку проекта и публикацию релизов. Детально ознакомиться с нашим решением можно на GitHub.


После загрузки расширения и заливки в основную ветку запускается пайплайн. В результате получается собранный проект расширения, который можно скачать и опубликовать в магазине приложений. Мы этот процесс автоматизировали, а дальше предлагаем рассмотреть, как опубликовать расширение вручную.
Загрузка приложения в Chrome Web Store
Создать учетную запись в консоли разработчика Google. Регистрация стоит 5 $.
На главной странице нажать + Добавить продукт и загрузить архив с расширением.
Заполнить форму описания. К ней надо приложить:
логотип и скриншоты расширения;
описание функций расширения;
обоснования для всех запрашиваемых разрешений (permissions) из manifest.json.

Чем больше разрешений запрашивает приложение, тем дольше идет проверка. Рекомендую оставить только необходимые и детально объяснить, зачем они нужны
Отправить расширение на проверку. По данным Chrome for Developers, время рассмотрения варьируется, но обычно занимает до трех дней. Обычно первый раз расширение проверяется дольше, у нас это заняло два дня. При последующих релизах проверка может занять около пары часов.
Еще в консоли разработчика доступна аналитика. Она показывает количество установок, просмотров и удалений.
Как расширение сделало нашу работу удобнее
Через Cloudhood можно задавать Request headers, которые автоматически применяются ко всем запросам браузера:

Оркестратор приложений на основе хедеров из запроса выбирает подходящую версию приложения для использования. Это позволяет командам независимо друг от друга открывать и тестировать изменения в dev-среде, задавать нужные версии микрофронтов и микросервисов. Такой подход обеспечивает устойчивость среды, позволяет синхронно вести разработку и тестирование.
Если у вас есть предложения, как улучшить наше расширение, ждем ваши issues и pull requests на GitHub.