Привет!

Меня зовут Саша Коновалов, я разработчик в компании Oxonit и наставник на программе «Мидл фронтенд-разработчик» в Яндекс.Практикуме.

На примере разработки расширения «Hello, Word» я расскажу, как разрабатывать кроссбраузерное расширение со знаниями современного фронтенда:

  • для чего нужно кроссбраузерное расширение;
  • как его разработать под разные браузеры;
  • модульность кода, как переиспользовать компоненты и как современный фронтенд помогает в разработке расширений;
  • какие инструменты можно использовать для сборки и публикации расширений (webpack, web-ext).

Если вам интересно посмотреть исходники, я разместил их на GitHub.

Структура расширения, о котором я расскажу в статье, близка к проекту SponsorBlock. Этот проект вдохновлял меня во время работы над собственным.

Итак, создадим расширение – Hello, Word!


Hello, Word — это расширение для перевода и запоминания слов. Оно стало прототипом моего пет-проекта Wordzzz, поэтому в посте я буду приводить примеры из него.


Рисунок 1. Описание функциональности

Что делает расширение?


  • выполняется на любом сайте (добавить функциональность на сайты — content-скрипт):
    – по выделению слова появляется иконка, при клике делается запрос на сервис перевода и открывается окошко перевода слова (1a);
    – также слово можно перевести, используя контекстное меню по правой кнопке мыши на выделенном слове (1b);
  • из окошка перевода можно добавить слово во внутренний словарь для истории и дальнейшего повторения (для передачи между различными частями расширения используется postMessage, для хранения слов — storage);
  • считает слова (нотификации) в background-скрипте (2);
  • помогает увидеть список внутреннего словаря в меню расширения и отредактировать его (всё та же связка postMessage + storage) (3);
  • позволяет тренироваться в изучении новых слов в отдельном окне (отдельная страница внутри расширения — learn-скрипт) (4).


Архитектура расширений



Схема архитектуры расширений. Источник: developer.mozilla.org

MDN Web Docs определяет расширение как набор файлов, который заботливо упакован для дальнейшего распространения и установки в браузер. Ниже указаны файлы, которые могут присутствовать в расширении.

manifest.json — обязательный файл для любого расширения. В нём содержатся имя расширения, требуемые разрешения и версия, а также указатели на другие файлы расширения.

Помимо этого в манифесте могут быть и указатели на другие файлы, например:

  • background pages — отвечает за долгоиграющую логику;
  • иконки и любые кнопки, которые расширение может определить;
  • sidebars, popups, and options pages — HTML-документы, предоставляющие содержимое для разных компонентов интерфейса пользователя;
  • content scripts — исполняемые на страницах JavaScript-сценарии расширения;
  • web-accessible resources — отвечает за то, чтобы контент вашего расширения стал видимым для скриптов и веб-страниц.

Наложим эту структуру на наш проект:


Рисунок 2. Архитектура расширения Hello, Word!

Архитектура проекта


Можно разрабатывать простые расширения, как в инструкции от Chrome. Мне же интересно расширить эту инструкцию для более сложных проектов, где надо добавить общие компоненты, системы сборки, кроссбраузерность, react, typescript — всё, без чего сложно представить современный фронтенд-проект.


Рисунок 3. Архитектура проекта Hello, Word!

Из рисунка выше видно, что проект состоит из нескольких частей:

  • Common — здесь хранятся общие компоненты, стили и т. д.;
  • Extension — само расширение Hello, Word!
  • Web — небольшой лендинг для проекта;
  • свой вариант может быть разным (даже другое расширение, почему нет?). В Wordzzz мы добавили ещё react-native-проект, дополнительно — сервер и разные интересные идеи и эксперименты.

Структура проекта


Управлять связями между пакетами помогает инструмент lerna (подробнее про настройку и использование можно прочитать на официальном сайте). Как и писал выше, в Common — общие стили и компоненты, общая статика (шрифт и иконки) в public-папке в корне. Web и Extension используют общие компоненты.


Рисунок 4: Структура проекта

В моем случае все пакеты собираются с помощью webpack. Для Extension собираются пять бандлов в дистрибутиве (в соответствии с Рис. 3), manifest.json, в котором указываются эти бандлы, и копируется статика.

Общие компоненты используются в соответствии с инструкцией lerna.

Для запуска расширения используется инструмент web-ext — он устанавливает расширение и позволяет подхватывать изменения кода в это установленное расширение.

Но у инструмента есть и ограничения: расширение может не загрузиться правильно в первый раз. В этом случае поможет сочетание с простым способом — обновлять вручную через панель расширений (в основном для изменений контент-скриптов).

Описание инструментов можно найти здесь.

Как достигается кроссбраузерность


1) Отдельные manifest под разные цели и браузеры


Собирается вебпаком c помощью небольшого написанного модуля под разные окружения и браузеры. В FF, например, нужно расширять наш исходный manifest.json добавлением browser_specific_settings.gecko.id.

2) Safari: конвертация кода в xcode-проект


Пользуюсь конвертером от Apple:

xcrun safari-web-extension-converter ./packages/extension/dist --project-location ./packages/extension/safari --app-name HelloWord --swift --bundle-identifier app.wordzzz.HelloWord

Я указал --bundle-identifier своего сайта app.wordzzz.HelloWord, вы укажете свой.

Если всё прошло успешно, то увидим такое сообщение:



После конвертации откроется проект и рекомендации не использовать некоторые функции (об этом ниже).

Жмём кнопку Run в XCode и смотрим, как всё работает.

Примечание: Может потребоваться включить «Разрешить неподписанные расширения» в меню «Разработка».

3) Проверка в CSS и немного про пути


До файлов в chromium-браузерах и FF разный относительный путь, поэтому я использовал css-селекторы:

/* firefox */
@-moz-document url-prefix() {
	.hw-icon__logo-content {
		background: url("../images/logo.svg") no-repeat;
	}
}

/* not firefox */
@supports not (-moz-appearance:none) {
	.hw-icon__logo-content {
		background: url("chrome-extension://*MSG@@extension_id*_/images/logo.svg") no-repeat;
	}
}

4) Проверка в JS


Тут ничего нового — проверка navigator.userAgent. Например, при публикации в AppStore меня просили убрать кнопку доната.

5) Рекомендации по (не-)использованию определённых функций



Полезные ссылки



Вместо заключения


Как видите, разработать расширение можно и с современным стеком технологий. Я описал свой стек, но можно использовать и другой, важнее было показать подход в целом.

В проекте Hello, Word ещё много чего можно сделать, например, покрыть типами и тестами, использовать препроцессоры и многое другое. Предлагайте в комментариях полезные идеи для улучшения проекта и свои решения и полезные инструменты для разработки расширений. Всем мир.