Aufgabenstellung: Ich soll mit npm in ein Repository, anders: einen lokalen Ordner, nicht nur eine Version von Bootstrap im Verzeichnis node_modules/ bereit stellen, sondern diverse herunter laden. So kann ich die scss/-Ordner der unterschiedlichen Versionen leichter abgleichen, anschauen, kopieren, nutzen und jederzeit bequem updaten bzw. erneuern; ohne, dass ich auf GitHub rumsuchen muss.

Das geht natürlich mit anderen npm-Paketen ebenso.

Meine Arbeitsumgebung

Auch, wenn diese nicht maßgeblich ist und jedwede Konsole-Variante möglich ist, auf der mit npm gearbeitet werden kann. Das geht ja auch mit Windows-Hausmitteln. Mag ich aber nicht.

Ich verwende WSL1 mit Debian-Linux auf Windows 10. Das Windows-Subsystem für Linux (WSL) ermöglicht es, ein "echtes" Linux mit Konsole an seinem Windows-Rechner zu betreiben. Version 1 verwende ich, da ich vornehmlich mit Windows-Laufwerken arbeiten will, was angeblich schneller mit dieser Version läuft als mit WSL-Version 2.

Meine unter Linux installierte npm-Version ist momentan halbwegs aktuell. Ich weiß nur, dass mit Uraltversionen das unten beschriebene Vorgehen nicht funktioniert. Die Version kann man in der Konsole prüfen mit:

npm -v

---ergibt bei mir gerade---
8.1.4

Ordner vorbereiten

Meine Repositories befinden sich in Windows im Hauptordner Z:\git-kram in diversen Unterordnern. Über die Linuxkonsole komme ich an dieses Verzeichnis via absoluten Pfad /mnt/z/git-kram heran.

Zuerst lege ich mir ein neues Verzeichnis an. Der Name soll sein bootstrapscssghsvs. Folgendes ist komplett unabhängig davon wie man den nun nennt.

Das ist zugleich mein Repository-Name, weil ich den Ordner später auch auf GitHub hochladen möchte. Das ist aber gar nicht unbedingt nötig und dient mir in diesem Fall nur als doppelte Sicherung.

Und wechsele in der Konsole auch gleich in dieses neue Verzeichnis:

cd /mnt/z/git-kram/
mkdir bootstrapscssghsvs
cd bootstrapscssghsvs

---Danach sehe ich---
volli@DELL-M6800:/mnt/z/git-kram/bootstrapscssghsvs$

Auch unter Stinknormal-Windows finde ich den neuen, leeren Ordner Z:\git-kram\bootstrapscssghsvs.

Die package.json für's npm

Als nächstes benötigen wir in unserem neuen Ordner eine Datei package.json. Diese enthält Informationen und Anweisungen für npm, Node etc.

Wer nun unbedingt möchte, feuert an der Konsole ein npm init ab und beantwortet die Fragen, die gestellt werden, um abschließend die neue Datei anzulegen und folgend "umzubiegen". Mich verwirrt dieses Prozedere eher und ich kopiere sie lieber aus einem anderen meiner Repositories und passe sie an  ;-) Viele abgefragte Angaben sind für mein Vorhaben sowieso nicht relevant. Außerdem legt es immer auch irgendwas mit index.js an, was ich gar nicht benötige. Ich tue also so, als hätte ich das init gar nicht erwähnt.

So sieht meine Grundversion der package.json aus.

{
	"name": "bootstrapscssghsvs",
	"version": "1.0.0",
	"description": "A collection of SCSS files of different Bootstrap versions. Not any more.",
	"scripts": {
		"test": "echo \"Error: no test specified\" && exit 1",
		"updateCheck": "npm outdated",
		"update": "npm update --save-dev"
	},
	"author": {
		"name": "G@HService Berlin Neukölln, Volkmar Volli Schlothauer",
		"url": "https://ghsvs.de"
	},
	"license": "ISC",
	"repository": {
		"type": "git",
		"url": "https://github.com/GHSVS-de/bootstrapscssghsvs.git"
	},
	"dependencies": {}
}

repository, license, author kann sich der geneigte Leser eigentlich sparen.

Ein erster Test

Oben in Zeile 6 sieht man ja, dass ich ein Test-Script "test" angelegt habe, neben weiteren, die unten noch zum Einsatz kommen.

In der Konsole schicke ich den Test mit einem npm run test ab:

npm run test

---Und das gibt aus und zeigt mir, dass die package.json funktioniert:

> bootstrapscssghsvs@1.0.0 test
> echo "Error: no test specified" && exit 1

Error: no test specified

Die zu ladenden Pakete (dependencies) definieren

In Zeile 19 obiger package.json sehen wir ein noch leeres "dependencies": {}. Hier kommen nun kommagetrennt die Anweisungen hinein, was npm uns in den Ordner node_modules herunterladen wird. Wenn der noch nicht existiert, wird er automatisch neu angelegt.

Hinweis: Der npm semver calculator kann hilfreich sein, wie man die jeweiligen Versionsangaben zu formulieren hat, um spezifisch diese oder jene "abzugreifen".

Vorspiele

Zuerst möchte ich immer die aktuellste Version von Bootstrap haben und befülle mein dependencies-Objekt erst mal nur mit "bootstrap": "*".

"dependencies": {
	"bootstrap": "*"
}

In die Konsole gebe ich ein: npm install. Nach etwas mehr oder weniger Wartezeit (WSL kann gelegentlich langsam sein, weil unter anderem der Virenscanner reichlich bremsen kann), finde ich im Ordner node_modules/ den Ordner bootstrap/. Schaue ich in die Datei node_modules/bootstrap/package.json, sehe ich, dass es zum Zeitpunkt des Schreibens die aktuelle Version 5.2.0 ist.

Hinweis: Weitere Ordner, die sich in node_modules/ befinden sind normal und interessieren mich für mein eigentliches Vorhaben nicht. Hintergrund ist, dass auch die heruntergeladenen Bootstrap-Pakete mehr oder weniger eigene Abhängigkeiten (= Dependencies) haben, die halt auch in diesem Verzeichnis landen.

Weiter geht's: Jetzt würde es mir aber besser gefallen, die aktuellste Bootstrap-Version im Ordner node_modules/bs-current zu finden.

Ich ändere die obige Dependency-Zeile nach "bs-current": "npm:bootstrap@*"

Der erste Teil ist mein Ziel-Wunschordner. Der zweite erzählt npm welches Paket online abgeholt werden soll und nach dem @ welche Version. Stern bedeutet halt das aktuellste.

Erneut gebe ich in die Konsole npm install ein. Danach findet sich tatsächlich ein neuer Ordner node_modules/bs-current und der alte node_modules/bootstrap ist verschwunden.

Etwas blödsinnig in meinem Szenario. Ich formuliere meine dependencies so um. Also 2 Zeilen.

"dependencies": {
	"bootstrap": "*",
	"bs-current": "npm:bootstrap@*"
}

Wieder npm install. Zwei Ordner mit identischem Inhalt (= aktuellste Version) wurden erzeugt:

  • node_modules/bs-current
  • node_modules/bootstrap

Jetzt aber richtig

Was ich in meinem finalen Bootstrap-Sammelsurium haben möchte sind fünf verschiedene Ordner mit jeweils spezifischer Version darin:

  1. Die immer aktuellste Version in Ordner bs-current.
  2. Die jeweils aktuellste Version 5.2.x in Ordner bs-52.
  3. Die jeweils aktuellste Version 5.1.x in Ordner bs-51.
  4. Die jeweils aktuellste Version 4.x in Ordner bs-4.
  5. Nur für diese Anleitung die allererste Version 0.0.1 in Ordner bs-first.

Meine Dependencies sehen folglich und mit Hilfe des oben erwähnten npm semver calculator, der mir beim Finden der richtigen Dacherlzeichen (^) und Tildenzeichen (~) geholfen hat, so aus:

"dependencies": {
	"bs-current": "npm:bootstrap@*",
	"bs-52": "npm:bootstrap@~5.2",
	"bs-51": "npm:bootstrap@~5.1",
	"bs-4": "npm:bootstrap@^4",
	"bs-first": "npm:bootstrap@0.0.1"
}

Schieße ich nun ein npm install ab und mache die Augen zu bezüglich irgendwelcher WARN und severity vulnerability (wer alte Pakete lädt, sieht so was schon mal und zumindest ich kann sie in meinem Szenario komplett weg ignorieren), habe ich anschließend tatsächlich meine fünf Wunschordner mit den verschiedenen Versionen.

  1. node_modules/bs-current
  2. node_modules/bs-52
  3. node_modules/bs-51
  4. node_modules/bs-4
  5. node_modules/bs-first

Updaten statt installieren

Wenn man jedes mal neu installiert, kann das bei umfangreicheren dependencies unnötig lange dauern. Deshalb habe ich in allen meinen Repository-package.json-Dateien im scripts-Block zwei Update-Zeilen dabei:

"scripts": {
	"test": "echo \"Error: no test specified\" && exit 1",
	"updateCheck": "npm outdated",
	"update": "npm update --save-dev"
}

Gebe ich in die Konsole npm run updateCheck ein, zeigt sie mir, ob eventuell was zu aktualisieren ist.

Wenn ja, feuere ich ein npm run update ab.

Sonstiges

Naürlich werde ich noch ein JavaScript schreiben, mit dem mir dann ausschließlich die scss-Ordner aus diesen unübersichtlichen node_modules-Unterordnern praktikabler für die tägliche Arbeit irgendwo hinkopiert werden.