Разработкой расширений к Chrome я занимаюсь давно и за это время я прошел целый путь от Greasemonkey юзер-скриптов до полноценного Angular-приложения в оболочке chrome-extension. Моя задача в том, что я патчу некоторые уже работающие сайты, чтобы изменить их функционал и автоматизировать некоторые процессы на этих сайтах. Иногда моё приложение разрастается до больших масштабов и поддерживать plain-js расширение становится сложно (в приложении много настроек, CRUD функционала и тд). И тут на помощь приходит Angular.
В этой статье я расскажу вам как я подружил Chrome Extension и Angular CLI, и наладил процесс разработки, а также с какими трудностями я сталкивался и как их решал.
Создаем новую папку и инициализируем в ней новое приложение
В стадии development Angular генерирует динамический html-файл, в котором происходит разработка, а хромовскому расширению нужно скармливать статичный html-файл, чтобы видеть результат работы. Конечно, можно отдельно собрать Ангуляровский проект и затем build-версию уже прописать в дополнение, но будет удобно, если это будет происходить автоматически.
После генерации нового приложения заходим в папку frontend и в файле package.json в раздел scripts добавляем новый скрипт для сборки нашего проекта
Обратите внимание на
Затем в корне проекта создаем еще одну папку extension и в ней создадим файл extension.js, который будет являться background-скриптом для нашего расширения. Текущая структура проекта:
Содержимое extension.js
Это будет browserAction, который откроет новую вкладку с нашим ангуляр-приложением, которое в собранном виде будет уже храниться по этому пути.
Теперь мы можем собрать наш проект в расширение. Заходим в chrome://extensions/, включаем режим разработчика и выбираем Загрузить распакованное расширение.
Удобство в том, что у нас есть почти hot-reloading. Мы запустили ng build с флагом watch и при изменении кода проект будет пересобираться и js-файлы будут подменяться. То есть, на странице расширения нам нужно всего лишь перезагрузить страницу и все обновления подхватятся. Это значительно ускоряет разработку дополнений.
Еще один момент, на котором я собаку съел, это что в конфигурации роутинга:
а также вернемся выше, base-href при билде мы прописали как /frontend/dist/frontend/index.html?/ — обратите внимание на знак вопроса перед последним слэшем. Дело в том, что на некоторых системах, при переходе по ссылкам в ангуляровском приложении, все крашится без этого знака. Судя по всему, статический веб-сервер движка хрома воспринимал изменение URL как запрос к другому файлу (даже несмотря на useHash: true) и при обновлении страницы возвращал 404 ошибку. Только в таком сочетании мне удалось добиться стабильной работы на всех системах.
Вернемся к package.json нашего приложения и добавим еще один скрипт
Это будет скрипт для сборки нашего приложения для production версии.
В Chrome Web Store постится zip-архив с расширением и чтобы упростить сборку я сделал скрипт для этих целей
Исходный код можно посмотреть тут.
Итого: мы создали заготовку для разработки Angular Chrome Extension с правильным роутингом и удобным обновлением/сборкой приложения.
P.S.: чтобы избавиться от такого /frontend/dist/frontend/ пути, можно настроить environment и в продакшн сборке по-другому прописывать путь, но это уже не ключевой момент.
В этой статье я расскажу вам как я подружил Chrome Extension и Angular CLI, и наладил процесс разработки, а также с какими трудностями я сталкивался и как их решал.
Создаем новую папку и инициализируем в ней новое приложение
mkdir new-project
cd new-project
ng new frontend --routing=true --skipGit=true --style=scss --skipTests=true
В стадии development Angular генерирует динамический html-файл, в котором происходит разработка, а хромовскому расширению нужно скармливать статичный html-файл, чтобы видеть результат работы. Конечно, можно отдельно собрать Ангуляровский проект и затем build-версию уже прописать в дополнение, но будет удобно, если это будет происходить автоматически.
После генерации нового приложения заходим в папку frontend и в файле package.json в раздел scripts добавляем новый скрипт для сборки нашего проекта
"developing": "ng build --watch --deploy-url /frontend/dist/frontend/ --base-href /frontend/dist/frontend/index.html?/"
Обратите внимание на
deploy-url
и base-href
.Затем в корне проекта создаем еще одну папку extension и в ней создадим файл extension.js, который будет являться background-скриптом для нашего расширения. Текущая структура проекта:
| new-project/
| | extension/
| | | extension.js
| | frontend/
| | | ...
Содержимое extension.js
const ANGULAR_HTML_URL = "../../frontend/dist/frontend/index.html";
chrome.browserAction.onClicked.addListener(function () {
chrome.tabs.create({
url: chrome.runtime.getURL(ANGULAR_HTML_URL)
});
});
Это будет browserAction, который откроет новую вкладку с нашим ангуляр-приложением, которое в собранном виде будет уже храниться по этому пути.
Добавим manifest.json в наш проект
{
"manifest_version": 2,
"name": "Simple Chrome Ext",
"description": "Simple Chrome Extension as an example",
"version": "1.00",
"author": "Bogdan",
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"background": {
"persistent": true,
"scripts": ["/extension/extension.js"]
},
"browser_action": {
"default_title": "Chrome ext"
}
}
Теперь мы можем собрать наш проект в расширение. Заходим в chrome://extensions/, включаем режим разработчика и выбираем Загрузить распакованное расширение.
Результат

Удобство в том, что у нас есть почти hot-reloading. Мы запустили ng build с флагом watch и при изменении кода проект будет пересобираться и js-файлы будут подменяться. То есть, на странице расширения нам нужно всего лишь перезагрузить страницу и все обновления подхватятся. Это значительно ускоряет разработку дополнений.
Еще один момент, на котором я собаку съел, это что в конфигурации роутинга:
{
useHash: true
}
а также вернемся выше, base-href при билде мы прописали как /frontend/dist/frontend/index.html?/ — обратите внимание на знак вопроса перед последним слэшем. Дело в том, что на некоторых системах, при переходе по ссылкам в ангуляровском приложении, все крашится без этого знака. Судя по всему, статический веб-сервер движка хрома воспринимал изменение URL как запрос к другому файлу (даже несмотря на useHash: true) и при обновлении страницы возвращал 404 ошибку. Только в таком сочетании мне удалось добиться стабильной работы на всех системах.
Вернемся к package.json нашего приложения и добавим еще один скрипт
"prod": "ng build --sourceMap false --prod true --deploy-url /frontend/dist/frontend/ --base-href /frontend/dist/frontend/index.html?/"
Это будет скрипт для сборки нашего приложения для production версии.
В Chrome Web Store постится zip-архив с расширением и чтобы упростить сборку я сделал скрипт для этих целей
update.sh
#!/bin/bash
rm -rf ./prod-build
mkdir -p prod-build/frontend
cd frontend
npm run prod
cd ..
cp -R ./frontend/dist ./prod-build/frontend
cp -R ./extension ./prod-build
cp ./manifest.json ./prod-build
zip -r prod-build{.zip,}
rm -rf ./prod-build
Исходный код можно посмотреть тут.
Итого: мы создали заготовку для разработки Angular Chrome Extension с правильным роутингом и удобным обновлением/сборкой приложения.
P.S.: чтобы избавиться от такого /frontend/dist/frontend/ пути, можно настроить environment и в продакшн сборке по-другому прописывать путь, но это уже не ключевой момент.