How To: Entwicklung von Cross-Browser-Extensions mit einer Codebase und tailwindcss
Für einen meiner Kunden durfte ich die Pflege und Weiterentwicklung einer bestehenden Browser-Extension für Firefox und Chrome übernehmen. Meine erste Aufgabe bestand darin Bugs zu beheben und die Extension von der Manifest V2 auf die Manifest V3 zu migrieren. Später folgte noch ein Redesign.
Zum Zeitpunkt der Übergabe wurde für Firefox sowie für Chrome jeweils die identische Codebase gepflegt (abgesehen von den Unterschieden in der manifest.json
). Damit ich bei der Pflege und Weiterentwicklung nicht jede Codeänderung doppelt vornehmen muss und Zeit spare, habe ich die Codebase zusammengefasst. Soweit, so gut. Sobald du aber, deine Extension lokal im Browser laufen lassen oder in den Extension-Store hochladen möchtest, kommst du mit der einen Codebase nicht weiter.
Wie mit der manifest.json
umgehen?
Chrome und Firefox haben unterschiedliche Anforderungen an die manifest.json
. Durch das Zusammenfassen der Codebase müssen wir die beiden manifest.json
in chrome.manifest.json
und firefox.manifest.json
umbenennen. Damit können weder Chrome noch Firefox was anfangen.
Die Lösung: Ein Skript, welches die Codebase nimmt und diese wieder in Chrome- und Firefox-spezifische Extensions aufsplittet. Beim Entwickeln kann es aber ganz schön anstrengend werden bei jeder Änderung dieses Skript erneut ausführen zu müssen. Wie wir das lösen, zeige ich dir gleich.
Wie mit Tailwind umgehen?
Für das Redesign habe ich mich für Tailwind entschieden. Auch hier haben wir das Problem, dass wir Tailwind nicht nach jeder Änderung am Style manuell ausführen wollen. Tailwind generiert das CSS-File basierend auf den verwendeten CSS-Klassen in deinem Projekt. Bedeutet, dass unter Umständen bestimmte CSS-Klassen aus deinen letzten Änderungen im Stylesheet nicht auftauchen.
Die Lösung: Tailwind- & bash
-Skript mit einem Watcher auslösen
Okay legen wir los. Für das Aufsplitten bauen wir uns ein bash
-Skript und legen es im bin
-Verzeichnis ab. Das Skript habe ich extensions-generate.sh
genannt. Es sieht wie folgt aus:
#!/bin/bash
DIR="dist/"
if [ -d "$DIR" ]; then
rm -r dist
echo "Deleted ${DIR} directory"
fi
mkdir -p dist/firefox/
mkdir -p dist/chrome/
cp -r [extension-folder] dist/firefox/
cp -r [extension-folder] dist/chrome/
mv dist/firefox/[extension-folder]/firefox.manifest.json dist/firefox/[extension-folder]/manifest.json
mv dist/chrome/[extension-folder]/chrome.manifest.json dist/chrome/[extension-folder]/manifest.json
Das Skript prüft zuerst ob es bereits ein dist
-Verzeichnis gibt. Falls ja, wird dieses gelöscht. Danach legt es die folgende Verzeichnisstruktur an:
dist
|- chrome
|- firefox
Als nächstes kopiert das Skript die Codebase jeweils in das Chrome- und das Firefox-Verzeichnis und benennt die chrome.manifest.json
sowie die firefox.manifest.json
zu manifest.json
um. [extension-folder]
ist ein Platzhalter. Den musst du mit dem Namen des Verzeichnisses austauschen in dem deine Codebase liegt.
Damit wir das Skript mit yarn extensions:generate
ausführen können, hinterlegen wir es noch in der package.json
:
"scripts": {
"extensions:generate": "./bin/extensions-generate.sh"
},
Es kann sein, dass du beim Ausführen diese Fehlermeldung erhältst: permission denied
. In dem Fall führst du im Terminal folgendes aus: chmod +x bin/build.sh
. Danach solltest du das Skript ausführen können.
Jetzt noch den Watcher einbauen
Um das Skript automatisch bei Codeänderungen auszuführen, müssen wir noch was ergänzen. Zur Hilfe installieren wir das npm
-Paket watch
mit yarn add watch -D
, dann passen wir noch die package.json
an:
"scripts": {
"extensions:generate": "./bin/extensions-generate.sh",
"extensions:watch": "watch './bin/extensions-generate.sh' ./[extension-folder]"
},
Okay, damit hätten wir den ersten Teil gelöst. Jetzt müssen wir noch das CSS mit Tailwind jedes Mal neu generieren lassen. Wie du Tailwind grundsätzlich einbindest, entnimmst du am besten der Dokumentation und wählst den Weg über die Tailwind CLI.
Tailwind nicht vergessen
Mit dem Befehl npx tailwindcss -i ./src/input.css -o ./dist/output.css
wird das CSS generiert. du musst unter Umständen die Pfade zu der Input- und Output-File anpassen. Bei mir sieht das so aus:
npx tailwindcss -i ./tailwind-input.css -o ./[extension-folder]/css/tailwind-output.css --minify
Damit ich diesen langen Befehl nicht immer eintippen muss, habe ich meine package.json
ergänzt:
"scripts": {
"css:generate": "npx tailwindcss -i ./tailwind-input.css -o ./[extension-folder]/css/tailwind-output.css --minify",
"extensions:generate": "./bin/extensions-generate.sh",
"extensions:watch": "watch './bin/extensions-generate.sh' ./[extension-folder]"
},
Tailwind und unser bash
-Skript müssen wir nacheinander ausführen. Dafür holen wir uns noch ein Helferlein mit yarn add npm-run-all -D
in unser Projekt. Mit dem Befehl yarn run-s ...
können wir dann mehrere Skripte nacheinander ausführen. Dabei berücksichtigt run-s
ob wir yarn
oder npm
nutzen. Jetzt ergänzen wir noch unsere package.json
mit diesem Befehl:
"scripts": {
"css:generate": "npx tailwindcss -i ./tailwind-input.css -o ./[extension-folder]/css/tailwind-output.css --minify",
"extensions:generate": "./bin/extensions-generate.sh",
"extensions:generate:all": "run-s css:generate extensions:generate",
"extensions:watch": "watch 'run-s css:generate extensions:generate' ./[extension-folder]"
},
extensions:generate:all
generiert einmalig das CSS und die jeweiligen Extensions. extensions:watch
beobachtet die Codebase und generiert bei jeder Änderung neues CSS und die Extensions.