3 min read

How To: Entwicklung von Cross-Browser-Extensions mit einer Codebase und tailwindcss

In diesem Artikel lernst du, wie du eine gemeinsame Codebase für deine Chrome- und Firefox-Extension nutzen kannst. Außerdem zeige ich dir, wie du tailwindcss einbindest und sich der Code während du entwickelst für die jeweilige Extension automatisch generiert.

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.