JavaScript >> Javascript-Tutorial >  >> Tags >> npm

So schreiben, testen und veröffentlichen Sie ein NPM-Paket

Wie Sie Ihr eigenes Paket erstellen, Tests schreiben, das Paket lokal ausführen und für NPM freigeben.

Erste Schritte

Für dieses Tutorial sollten Sie sicherstellen, dass Sie Node.js auf Ihrem Computer installiert haben (die neueste LTS-Version wird empfohlen – zum Zeitpunkt des Schreibens 16.13.1). Wenn Sie Node.js noch nicht installiert haben, lesen Sie zuerst dieses Tutorial.

Ein Projekt einrichten

Als Erstes richten wir einen neuen Ordner für unser Paket auf unserem Computer ein.

Terminal

mkdir package-name

Als nächstes wollen wir 01 in diesen Ordner und erstellen Sie eine 13 Datei:

Terminal

cd package-name && npm init -f

Hier, 22 weist NPM (Node Package Manager, das Tool, das wir zum Veröffentlichen unseres Pakets verwenden) an, ein neues Projekt zu initialisieren und einen 38 zu erstellen Datei in dem Verzeichnis, in dem der Befehl ausgeführt wurde. Die 41 steht für "force" und weist NPM an, eine Vorlage 54 auszuspucken Datei. Wenn Sie 68 ausschließen , NPM hilft Ihnen beim Erstellen von 72 Datei mit ihrem Schritt-für-Schritt-Assistenten.

Sobald Sie eine 81 haben Als Nächstes möchten wir eine geringfügige Änderung an der Datei vornehmen. Wenn Sie es öffnen, möchten wir ein spezielles Feld 99 hinzufügen an das Objekt, das auf den Wert "Modul" als Zeichenfolge gesetzt ist, wie folgt:

{
  "type": "module",
  "name": "@cheatcodetuts/calculator",
  "version": "0.0.0",
  "description": "",
  "main": "./dist/index.js",
  "scripts": { ... },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": { ... }
}

Ganz oben im JSON-Objekt haben wir 105 hinzugefügt . Wenn unser Code ausgeführt wird, teilt dies Node.js mit, dass wir erwarten, dass die Datei die ES-Modul-Syntax (ECMAScript-Modul oder kurz ESM) verwendet, im Gegensatz zur Common JS-Syntax. ESM verwendet den modernen 116 und 127 Syntax, während CJS den 136 verwendet -Anweisung und 147 Syntax. Wir bevorzugen einen modernen Ansatz, also durch Setzen von 154 aktivieren wir die Unterstützung für die Verwendung von 163 und 174 in unserem Code.

Danach wollen wir als nächstes zwei Ordner innerhalb erstellen unseres Paketordners:180 und 193 .

  • 203 wird die "Quell"-Dateien für unser Paket enthalten.
  • 216 enthält die erstellten (kompilierten und minimierten) Dateien für unser Paket (diese werden andere Entwickler in ihre App laden, wenn sie unser Paket installieren).

Innerhalb von 226 Verzeichnis möchten wir einen 237 erstellen Datei. Hier schreiben wir den Code für unser Paket. Später sehen wir uns an, wie wir diese Datei nehmen und erstellen, wobei die erstellte Kopie automatisch in 245 ausgegeben wird .

/src/index.js

export default {
  add: (n1, n2) => {
    if (isNaN(n1) || isNaN(n2)) {
      throw new Error('[calculator.add] Passed arguments must be a number (integer or float).');
    }

    return n1 + n2;
  },
  subtract: (n1, n2) => {
    if (isNaN(n1) || isNaN(n2)) {
      throw new Error('[calculator.subtract] Passed arguments must be a number (integer or float).');
    }

    return n1 - n2;
  },
  multiply: (n1, n2) => {
    if (isNaN(n1) || isNaN(n2)) {
      throw new Error('[calculator.multiply] Passed arguments must be a number (integer or float).');
    }

    return n1 * n2;
  },
  divide: (n1, n2) => {
    if (isNaN(n1) || isNaN(n2)) {
      throw new Error('[calculator.divide] Passed arguments must be a number (integer or float).');
    }

    return n1 / n2;
  },
};

Für unser Paket erstellen wir einen einfachen Taschenrechner mit vier Funktionen:250 , 263 , 272 , und 289 wobei jede zwei Zahlen akzeptiert, um ihre jeweilige mathematische Funktion auszuführen.

Die Funktionen hier sind nicht besonders wichtig (hoffentlich ist ihre Funktionalität klar). Was wir wirklich worauf Sie achten möchten ist der 295 oben und die 305 Linien innerhalb jeder Funktion.

Beachten Sie, dass wir, anstatt jede unserer Funktionen einzeln zu definieren, sie für ein einzelnes Objekt definiert haben, das aus unserem 319 exportiert wird Datei. Das Ziel hier ist, unser Paket in eine App wie diese zu importieren:

import calculator from 'package-name';

calculator.add(1, 3);

Hier ist das exportierte Objekt 325 und auf jede Funktion (in JavaScript werden für ein Objekt definierte Funktionen als "Methoden" bezeichnet) wird über dieses Objekt zugegriffen, wie wir oben sehen. Hinweis :So soll sich unser Beispielpaket verhalten, aber Ihr Paket kann sich anders verhalten – das ist alles zum Beispiel.

Konzentration auf 336 Beachten Sie, dass diese alle nahezu identisch sind. Das Ziel hier ist zu sagen "wenn 348 Argument oder 350 Argumente werden nicht als Zahlen (Ganzzahlen oder Gleitkommazahlen) übergeben, werfen einen Fehler."

Warum machen wir das? Überlegen Sie, was wir tun:Wir bauen ein Paket, das andere verwenden können. Dies unterscheidet sich davon, wie wir unseren eigenen Code schreiben könnten, bei dem Eingaben vorhersehbar oder kontrolliert sind. Bei der Entwicklung eines Pakets müssen wir uns des möglichen Missbrauchs dieses Pakets bewusst sein. Wir können dies auf zweierlei Weise erklären:indem wir eine wirklich gute Dokumentation schreiben, aber auch, indem wir unseren Code fehlertolerant und lehrreich machen.

Da es sich bei unserem Paket um einen Taschenrechner handelt, können wir hier dem Benutzer helfen, das Paket korrekt zu verwenden, indem wir eine strenge Anforderung stellen, dass er uns Zahlen zur Durchführung von Berechnungen übergibt. Wenn dies nicht der Fall ist, geben wir einen Hinweis darauf, was falsch gemacht wurde und wie das Problem auf Codeebene behoben werden kann . Dies ist wichtig für die Paketübernahme. Je entwicklerfreundlicher Ihr Code ist, desto wahrscheinlicher ist es, dass Ihr Paket von anderen verwendet wird.

Um diesen Punkt weiter voranzutreiben, werden wir als Nächstes lernen, wie man einige Tests für unser Paket schreibt und wie man sie ausführt.

Schreiben von Tests für Ihren Paketcode

Wir möchten so viel Vertrauen wie möglich in unseren Code haben, bevor wir ihn anderen Entwicklern zur Verfügung stellen. Obwohl wir dem, was wir geschrieben haben, einfach blind vertrauen können, ist dies nicht weise. Stattdessen können wir vor der Veröffentlichung unseres Pakets automatisierte Tests schreiben, die einen Benutzer simulieren, der unser Paket richtig (oder unsachgemäß) verwendet, und sicherstellen, dass unser Code wie erwartet reagiert.

Um unsere Tests zu schreiben, werden wir die Jest-Bibliothek von Facebook verwenden. Jest ist ein einzigartiges Tool, da es Folgendes kombiniert:

  • Funktionalität zum Erstellen von Testsuiten und individuellen Tests.
  • Funktionalität zum Durchführen von Behauptungen innerhalb von Tests.
  • Funktionalität zum Ausführen von Tests.
  • Funktionalität zum Melden der Testergebnisse.

Traditionell werden uns diese Tools über mehrere unabhängige Pakete zur Verfügung gestellt. Jest macht das Einrichten einer Testumgebung mühelos, indem es sie alle miteinander kombiniert. Um Jest zu unserem eigenen Paket hinzuzufügen, müssen wir seine Pakete über NPM (Meta!) installieren:

Terminal

npm install -D jest jest-cli

Hier sagen wir, dass Sie 365 installieren sollen und sein 377 Paket (letzteres ist die Befehlszeilenschnittstelle, die wir zum Ausführen von Tests verwenden) als reine Entwicklungsabhängigkeiten (durch Übergeben des 385 Flag auf 392 ). Das bedeutet, dass wir Jest nur in der Entwicklung verwenden wollen und nicht möchten, dass es als Abhängigkeit hinzugefügt wird, die neben unserem eigenen Paket im Code unseres Benutzers installiert wird.

/Paket.json

{
  "type": "module",
  "name": "@cheatcodetuts/calculator",
  "version": "0.0.0",
  "description": "",
  "scripts": {
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
    "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "jest": "^27.4.3",
    "jest-cli": "^27.4.3",
  }
}

Nun zu den Details. Hier in unserem 407 -Datei möchten wir unserem 412 zwei Zeilen hinzufügen Objekt. Diese 421 sind als "NPM-Skripte" bekannt, die, wie der Name schon sagt, wiederverwendbare Befehlszeilenskripte sind, die wir mit NPMs 437 ausführen können Funktion im Terminal.

Hier fügen wir 443 hinzu und 456 . Das erste Skript wird verwendet, um unsere Tests einmal auszuführen und einen Bericht zu generieren, während 464 führt unsere Tests einmal und dann erneut aus, wenn sich eine Testdatei (oder der zugehörige Code) ändert. Ersteres ist nützlich für eine schnelle Überprüfung der Dinge vor der Bereitstellung und letzteres ist nützlich, um Tests während der Entwicklung durchzuführen.

Betrachten wir den 475 genau Skript 480 Wir führen das auf eine seltsame Weise durch. Normalerweise könnten wir unser Skript als nichts anderes als 495 schreiben (buchstäblich 507 ) und es würde funktionieren, aber da wir unsere Tests mit ES-Modulen schreiben möchten (im Gegensatz zu Common JS), müssen wir dies in Jest aktivieren, genau wie wir es hier in unserem 516 für unseren Paketcode.

Dazu müssen wir Jest direkt über Node.js ausführen, damit wir den 529 übergeben können Flag für Node.js (wird von Jest benötigt, da die APIs, die sie zur Implementierung der ESM-Unterstützung verwenden, es immer noch als experimentelle Funktion betrachten).

Da wir Node verwenden, um Jest auszuführen (und nicht den 539 ist 543 Befehl direkt), müssen wir auch direkt auf die Binärversion von Jest verweisen (technisch gesehen ist dies das, was 557 weist für uns über 562 auf aber wegen der Flaggenpflicht müssen wir direkt gehen).

Der 570 Befehl ist nahezu identisch. Der einzige Unterschied besteht darin, dass wir am Ende den 580 hinzufügen müssen Flag, das Jest anweist, nach dem ersten Lauf weiterzulaufen und auf Änderungen zu achten.

/src/index.test.js

import calculator from './index';

describe('index.js', () => {
  test('calculator.add adds two numbers together', () => {
    const result = calculator.add(19, 88);
    expect(result).toEqual(107);
  });
});

Wenn es darum geht, unsere Tests zu schreiben, führt Jest automatisch alle Tests aus, die sich innerhalb eines 592 befinden Datei, in der 605 kann jeder Name sein, den wir wollen. Oben benennen wir unsere Testdatei so, dass sie mit der Datei übereinstimmt, in der sich unser Paketcode befindet:618 . Die Idee dabei ist, dass wir unseren Testcode neben dem echten Code halten wollen, den er testen soll.

Das mag verwirrend klingen, aber bedenken Sie, was wir tun:Wir versuchen, einen realen Benutzer zu simulieren, der unseren Code von seiner Anwendung aus aufruft. Das sind Tests in der Programmierung. Die Tests selbst sind nur die Mittel, die wir verwenden, um den Prozess zu automatisieren (z. B. im Gegensatz zu einer Tabelle mit manuellen Schritten, die wir befolgen und von Hand ausführen würden).

Oben besteht unsere Testdatei aus zwei Hauptteilen:einer Suite und einen oder mehrere Tests . Beim Testen repräsentiert eine "Suite" eine Gruppe zusammengehöriger Tests. Hier definieren wir eine einzelne Suite, um unseren 622 zu beschreiben Datei mit dem 630 Funktion im Scherz. Diese Funktion benötigt zwei Argumente:den Namen der Suite als Zeichenfolge (wir verwenden nur den Namen der Datei, die wir testen) und eine aufzurufende Funktion, in der unsere Tests definiert sind.

Ein Test folgt einem ähnlichen Aufbau. Es nimmt eine Beschreibung des Tests als Zeichenfolge für sein erstes Argument und dann eine Funktion, die aufgerufen wird, um den Test auszuführen.

Konzentration auf 642 Funktion, die wir hier haben, haben wir als Beispiel einen Test hinzugefügt, der unseren 656 sicherstellt funktioniert wie beabsichtigt und addiert zwei Zahlen zusammen, um die richtige Summe zu erhalten. Um den eigentlichen Test zu schreiben (in der Testsprache als „Ausführung“ bekannt), nennen wir unseren 665 Funktion, die zwei Zahlen übergibt und die Summe in der Variablen 671 speichert . Als Nächstes verifizieren wir dass die Funktion den erwarteten Wert zurückgegeben hat.

Hier erwarten wir 681 gleich 697 Das ist die Summe, die wir erwarten würden, wenn sich unsere Funktion richtig verhält. In Jest (und jeder Testbibliothek) können wir einem Test mehrere Assertionen hinzufügen, wenn wir dies wünschen. Auch hier ändert sich, genau wie beim eigentlichen Code in unserem Paket, das Was/Wann/Wie/Warum basierend auf der Absicht Ihres Codes.

Lassen Sie uns einen weiteren Test hinzufügen, um das Schlechte oder Unglückliche zu überprüfen Pfad für unsere 701 Funktion:

/src/index.test.js

import calculator from './index';

describe('index.js', () => {
  test('calculator.add throws an error when passed arguments are not numbers', () => {
    expect(() => {
      calculator.add('a', 'b');
    }).toThrow('[calculator.add] Passed arguments must be a number (integer or float).');
  });

  test('calculator.add adds two numbers together', () => {
    const result = calculator.add(19, 88);
    expect(result).toEqual(107);
  });
});

Hier etwas anders. Denken Sie daran, dass wir früher in unserem Paketcode eine Überprüfung hinzugefügt haben, um sicherzustellen, dass die Werte, die an jede unserer Taschenrechnerfunktionen übergeben wurden, Zahlen als Argumente übergeben wurden (wobei andernfalls ein Fehler ausgegeben wurde). Hier wollen wir testen, ob tatsächlich ein Fehler geworfen wird, wenn ein Benutzer die falschen Daten übergibt.

Das ist wichtig! Nochmals, wenn wir Code schreiben, den andere in ihrem eigenen Projekt verwenden, wollen wir so sicher wie möglich sein, dass unser Code das tut, was wir erwarten (und was wir anderen Entwicklern sagen, was wir von ihm erwarten).

Da wir hier überprüfen möchten, ob unsere Taschenrechnerfunktion einen Fehler auslöst, übergeben wir eine Funktion an unseren 712 und rufen Sie unsere Funktion innerhalb von that auf Funktion und übergibt ihr schlechte Argumente. Wie der Test sagt, erwarten wir 724 einen Fehler auszugeben, wenn die übergebenen Argumente keine Zahlen sind. Da wir hier zwei Strings übergeben, erwarten wir, dass die Funktion 732 ist die die Funktion an 748 übergab wird "fangen" und verwenden, um zu bewerten, ob die Behauptung wahr ist, indem der 750 verwendet wird Assertion-Methode.

Das ist der Kern des Schreibens unserer Tests. Werfen wir einen Blick auf die vollständige Testdatei (identische Konventionen werden nur für jede einzelne Taschenrechnerfunktion wiederholt).

/src/index.test.js

import calculator from './index';

describe('index.js', () => {
  test('calculator.add throws an error when passed argumen ts are not numbers', () => {
    expect(() => {
      calculator.add('a', 'b');
    }).toThrow('[calculator.add] Passed arguments must be a number (integer or float).');
  });

  test('calculator.subtract throws an error when passed arguments are not numbers', () => {
    expect(() => {
      calculator.subtract('a', 'b');
    }).toThrow('[calculator.subtract] Passed arguments must be a number (integer or float).');
  });

  test('calculator.multiply throws an error when passed arguments are not numbers', () => {
    expect(() => {
      calculator.multiply('a', 'b');
    }).toThrow('[calculator.multiply] Passed arguments must be a number (integer or float).');
  });

  test('calculator.divide throws an error when passed arguments are not numbers', () => {
    expect(() => {
      calculator.divide('a', 'b');
    }).toThrow('[calculator.divide] Passed arguments must be a number (integer or float).');
  });

  test('calculator.add adds two numbers together', () => {
    const result = calculator.add(19, 88);
    expect(result).toEqual(107);
  });

  test('calculator.subtract subtracts two numbers', () => {
    const result = calculator.subtract(128, 51);
    expect(result).toEqual(77);
  });

  test('calculator.multiply multiplies two numbers', () => {
    const result = calculator.multiply(15, 4);
    expect(result).toEqual(60);
  });

  test('calculator.divide divides two numbers', () => {
    const result = calculator.divide(20, 4);
    expect(result).toEqual(5);
  });
});

Für jede Taschenrechnerfunktion haben wir das gleiche Muster wiederholt:Überprüfen Sie, ob ein Fehler ausgelöst wird, wenn die übergebenen Argumente keine Zahlen sind, und erwarten Sie, dass die Funktion das richtige Ergebnis basierend auf der beabsichtigten Methode (Addieren, Subtrahieren, Multiplizieren oder Dividieren) zurückgibt. .

Wenn wir dies in Jest ausführen, sollten wir sehen, dass unsere Tests ausgeführt (und bestanden) werden:

Das war es für unsere Tests und den Paketcode. Jetzt sind wir bereit, in die letzten Phasen der Vorbereitung unseres Pakets für die Veröffentlichung überzugehen.

Erstellen unseres Codes

Obwohl wir diesen Code technisch jetzt veröffentlichen könnten, möchten wir auf zwei Dinge achten:ob das eigene Projekt eines Entwicklers unseren Paketcode unterstützt oder nicht, und die Größe des Codes.

Im Allgemeinen ist es gut, ein Build-Tool für Ihren Code zu verwenden, um bei diesen Problemen zu helfen. Für unser Paket verwenden wir den 760 Paket:ein einfaches und unglaublich schnelles Build-Tool für JavaScript, das in Go geschrieben wurde. Fügen wir es zunächst als Abhängigkeit zu unserem Projekt hinzu:

Terminal

npm install -D esbuild

Auch hier brauchen wir, wie wir bereits bei Jest gelernt haben, nur 779 in der Entwicklung, also verwenden wir den 780 Befehl, um das Paket in unserem 795 zu installieren .

/Paket.json

{
  "type": "module",
  "name": "@cheatcodetuts/calculator",
  "version": "0.0.0",
  "description": "",
  "main": "./dist/index.js",
  "scripts": {
    "build": "./node_modules/.bin/esbuild ./src/index.js --format=esm --bundle --outfile=./dist/index.js --platform=node --target=node16.3 --minify",
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
    "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "esbuild": "^0.14.1",
    "jest": "^27.4.3",
    "jest-cli": "^27.4.3",
    "semver": "^7.3.5"
  }
}

Ähnlich wie oben bei Jest, zurück in unserem 806 wollen wir ein weiteres Skript hinzufügen, dieses Mal mit dem Namen 818 . Dieses Skript ist für den Aufruf von 823 verantwortlich um die gebaute Kopie unseres Paketcodes zu generieren.

./node_modules/.bin/esbuild ./src/index.js --format=esm --bundle --outfile=./dist/index.js --platform=node --target=node16.3 --minify

Um 836 anzurufen , wieder, ähnlich wie wir Jest ausgeführt haben, beginnen wir unser Skript mit 846 . Hier der 859 am Anfang ist eine Kurzform, um zu sagen:"Führe das Skript in diesem Pfad aus" und geht davon aus, dass die Datei in diesem Pfad ein Shell-Skript enthält (beachten Sie, dass wir dies aus dem 860 importieren Ordner über 870 mit dem 888 script werden automatisch als Teil von 893 installiert ).

Wenn wir diese Funktion aufrufen, übergeben wir als erstes Argument den Pfad zu der Datei, die sie erstellen soll, in diesem Fall:906 . Als nächstes verwenden wir einige optionale Flags, um 915 mitzuteilen wie der Build ausgeführt wird und wo die Ausgabe gespeichert wird. Wir möchten Folgendes tun:

  • Verwenden Sie den 922 -Flag, um sicherzustellen, dass unser Code mit der ESM-Syntax erstellt wird.
  • Verwenden Sie den 938 Flag, um 949 mitzuteilen um externes JavaScript in die Ausgabedatei zu bündeln (für uns nicht erforderlich, da wir in diesem Paket keine Abhängigkeiten von Drittanbietern haben, aber für Sie selbst gut zu wissen).
  • Verwenden Sie den 953 -Flag zum Speichern des endgültigen Builds im 968 Ordner, den wir zuvor erstellt haben (unter Verwendung desselben Dateinamens wie für unseren Paketcode).
  • Stellen Sie den 979 ein Flag auf 982 damit 991 weiß, wie man alle eingebauten Node.js-Abhängigkeiten richtig behandelt.
  • Stellen Sie den 1008 ein Flag auf die Node.js-Version, auf die wir unseren Build ausrichten möchten. Dies ist die Version von Node.js, die auf meinem Computer ausgeführt wird, während ich dieses Tutorial schreibe, aber Sie können sie nach Bedarf anpassen, basierend auf den Anforderungen Ihres eigenen Pakets.
  • Verwenden Sie den 1010 Flag, um 1025 mitzuteilen um den ausgegebenen Code zu verkleinern.

Das letzte 1035 vereinfacht unseren Code und komprimiert ihn auf die kleinstmögliche Version, um sicherzustellen, dass unser Paket so leicht wie möglich ist.

Das ist alles, was wir tun müssen. Überprüfen Sie, ob Ihr Skript korrekt ist, und führen Sie dann in Ihrem Terminal (aus dem Stammverzeichnis Ihres Paketordners) Folgendes aus:

Terminal

npm run build

Nach einigen Millisekunden (1040 ist unglaublich schnell), sollten Sie eine Meldung sehen, dass der Build abgeschlossen ist und wenn Sie in 1052 nachsehen Ordner, sollten Sie einen neuen 1064 sehen Datei, die die kompilierte, verkleinerte Version unseres Paketcodes enthält (diese ist nicht für Menschen lesbar).

Ganz schnell, bevor wir diesen Schritt als "fertig" bezeichnen, müssen wir unseren 1070 aktualisieren ist 1084 Feld, um sicherzustellen, dass NPM Entwickler auf die richtige Version unseres Codes hinweist, wenn sie ihn in ihre eigenen Projekte importieren:

/Paket.json

{
  "type": "module",
  "name": "@cheatcodetuts/calculator",
  "version": "0.0.0",
  "description": "",
  "main": "./dist/index.js",
  "scripts": {
    "build": "./node_modules/.bin/esbuild ./src/index.js --format=esm --bundle --outfile=./dist/index.js --platform=node --target=node16.3 --minify",
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
    "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "esbuild": "^0.14.1",
    "jest": "^27.4.3",
    "jest-cli": "^27.4.3",
    "semver": "^7.3.5"
  }
}

Hier möchten wir auf den Teil 1090 achten . Dadurch wird sichergestellt, dass bei der Installation unseres Pakets der Code ausgeführt wird, der sich in dem hier angegebenen Pfad befindet. Wir möchten, dass dies unser gebautes ist kopieren (über 1104 ) und nicht unser Quellcode, da die erstellte Kopie, wie wir oben angedeutet haben, sowohl kleiner ist als auch eher von der App des Entwicklers unterstützt wird.

Schreiben eines Freigabeskripts

Für den Endspurt wollen wir uns nun die langjährige Arbeit an unserem Paket etwas erleichtern. Technisch gesehen können wir unser Paket über NPM freigeben, indem wir einfach 1118 verwenden . Dies funktioniert zwar, verursacht aber ein Problem:Wir haben keine Möglichkeit, unser Paket lokal zu testen. Ja, wir können den Code über unsere automatisierten Tests in Jest testen, aber es ist immer gut zu überprüfen, ob unser Paket wie beabsichtigt funktioniert, wenn es in der Anwendung eines anderen Entwicklers verwendet wird (nochmals:Bei diesem Prozess geht es darum, das Vertrauen zu erhöhen, dass unser Code wie beabsichtigt funktioniert). .

Leider bietet NPM selbst keine lokale Testmöglichkeit an. Obwohl wir ein Paket über NPM lokal auf unserem Computer installieren können, ist der Prozess etwas chaotisch und fügt Verwirrung hinzu, die zu Fehlern führen kann.

Im nächsten Abschnitt werden wir etwas über ein Tool namens Verdaccio (vur-dah-chee-oh) lernen, das uns hilft, einen nachgebildeten NPM-Server auf unserem Computer zu betreiben, auf dem wir unser Paket „scheinveröffentlichen“ können (ohne es freizugeben unseren Code an die Öffentlichkeit).

Als Vorbereitung darauf werden wir jetzt ein Freigabeskript für unser Paket schreiben. Dieses Release-Skript ermöglicht es uns, dynamisch...

  1. Versionieren Sie unser Paket, indem Sie unseren 1123 aktualisieren ist 1138 Feld.
  2. Geben Sie unser Paket bedingt an unseren Verdaccio-Server oder an NPM zur öffentlichen Veröffentlichung frei.
  3. Vermeiden Sie, dass die Versionsnummer unseres öffentlichen Pakets nicht mehr mit unserer Entwicklungsversionsnummer synchronisiert wird.

Für den Anfang ist #3 ein Hinweis. Wir möchten unseren 1140 öffnen Datei erneut und fügen Sie ein neues Feld hinzu:1159 , indem Sie ihn auf 1162 setzen .

/Paket.json

{
  "type": "module",
  "name": "@cheatcodetuts/calculator",
  "version": "0.0.0",
  "developmentVersion": "0.0.0",
  "description": "",
  "main": "./dist/index.js",
  "scripts": {
    "build": "./node_modules/.bin/esbuild ./src/index.js --format=esm --bundle --outfile=./dist/index.js --platform=node --target=node16.3 --minify",
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
    "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "esbuild": "^0.14.1",
    "jest": "^27.4.3",
    "jest-cli": "^27.4.3"
  }
}

Oben in unserer Datei, direkt unter dem 1179 Feld haben wir 1183 hinzugefügt und setzen Sie ihn auf 1190 . Es ist wichtig zu beachten, dass developmentVersion ein nicht standardmäßiges Feld in einer package.json-Datei ist . Dieses Feld ist nur für uns und wird von NPM nicht erkannt.

Unser Ziel mit diesem Feld – wie wir gleich sehen werden – ist es, eine Version unseres Pakets zu haben, die von der Produktionsversion unabhängig ist. Dies liegt daran, dass NPM immer dann versucht, unser Paket zu versionieren, wenn wir unser Paket veröffentlichen (lokal oder für die Produktion/öffentlich). Da wir wahrscheinlich mehrere Entwicklungsversionen haben, möchten wir vermeiden, Produktionsversionen von so etwas wie 1202 zu überspringen bis 1214 wobei die 49 Veröffentlichungen zwischen den beiden nur unsere Entwicklungsversion des Pakets testen (und nicht die tatsächlichen Änderungen am Kernpaket widerspiegeln).

Um dieses Szenario zu vermeiden, verhandelt unser Freigabeskript zwischen diesen beiden Versionen basierend auf dem Wert von 1223 und halten Sie unsere Versionen sauber.

/release.js

import { execSync } from "child_process";
import semver from "semver";
import fs from 'fs';

const getPackageJSON = () => {
  const packageJSON = fs.readFileSync('./package.json', 'utf-8');
  return JSON.parse(packageJSON);
};

const setPackageJSONVersions = (originalVersion, version) => {
  packageJSON.version = originalVersion;
  packageJSON.developmentVersion = version;
  fs.writeFileSync('package.json', JSON.stringify(packageJSON, null, 2));
};

const packageJSON = getPackageJSON();
const originalVersion = `${packageJSON.version}`;
const version = semver.inc(
  process.env.NODE_ENV === 'development' ? packageJSON.developmentVersion : packageJSON.version,
  'minor'
);

const force = process.env.NODE_ENV === "development" ? "--force" : "";

const registry =
  process.env.NODE_ENV === "development"
    ? "--registry http://localhost:4873"
    : "";

try {
  execSync(
    `npm version ${version} --allow-same-version ${registry} && npm publish --access public ${force} ${registry}`
  );
} catch (exception) {
  setPackageJSONVersions(originalVersion, version);
}

if (process.env.NODE_ENV === 'development') {
  setPackageJSONVersions(originalVersion, version);
}

Dies ist die Gesamtheit unseres Release-Skripts. Ganz schnell, ganz oben werden Sie eine zusätzliche Abhängigkeit bemerken, die wir 1235 hinzufügen müssen :

Terminal

npm install -D semver

Wenn wir uns auf die Mitte unseres Release-Skriptcodes konzentrieren, müssen wir als Erstes den aktuellen Inhalt unseres 1248 abrufen Datei in den Speicher geladen. Dazu haben wir am Anfang unserer Datei eine Funktion 1251 hinzugefügt der den Inhalt unserer Datei als String mit 1263 in den Speicher liest und parst diese Zeichenfolge dann mithilfe von 1273 in ein JSON-Objekt .

Als nächstes mit unserem 1285 Datei in die Variable 1299 geladen speichern oder „kopieren“ wir den 1308 , und achten Sie darauf, den Wert innerhalb einer Zeichenfolge mit Backticks zu speichern (dies kommt ins Spiel, wenn wir die Version in unserem 1314 dynamisch zurücksetzen Datei später im Skript).

Danach mit 1324 Paket, das wir gerade installiert haben, möchten wir die Version für unser Paket erhöhen. Hier, 1331 ist die Abkürzung für semantische Version, die ein weithin akzeptierter Standard zum Schreiben von Softwareversionen ist. Der 1342 Paket, das wir hier verwenden, hilft uns, semantische Versionsnummern zu generieren (wie 1355 oder 1367 ) und parsen sie zur Auswertung in unserem Code.

Hier, 1375 wurde entwickelt, um die semantische Version, die wir als erstes Argument übergeben, zu inkrementieren, basierend auf der "Regel", die wir als zweites Argument übergeben. Hier sagen wir "wenn 1381 Entwicklung ist, möchten wir den 1390 erhöhen von unserem 1400 und wenn nicht, wollen wir den normalen 1410 erhöhen Feld aus unserem 1421 ."

Für das zweite Argument verwenden wir hier den 1438 Regel, die 1449 mitteilt um unsere Version basierend auf der mittleren Zahl in unserem Code zu erhöhen. Das ist also klar, eine semantische Version hat drei Nummern:

major.minor.patch

Standardmäßig setzen wir sowohl unseren 1451 und 1464 bis 1479 Wenn wir also zum ersten Mal eine Version ausführen, erwarten wir, dass diese Nummer auf 1489 erhöht wird und dann 1492 und so weiter.

Mit unserer neuen Version im 1500 gespeichert Variable, als nächstes müssen wir zwei weitere Entscheidungen treffen, die beide auf dem Wert von 1511 basieren . Die erste ist zu entscheiden, ob wir erzwingen wollen die Veröffentlichung unseres Pakets (dies erzwingt die Veröffentlichung der Version) und der zweite entscheidet, in welcher Registry wir veröffentlichen wollen (unserem Verdaccio-Server oder der Haupt-NPM-Registry). Für 1529 -Variable gehen wir davon aus, dass Verdaccio an seinem Standardport auf localhost ausgeführt wird, also setzen wir den 1534 Flag auf 1549 wobei 1555 ist der standardmäßige Verdaccio-Port.

Weil wir diese Variablen einbetten 1565 und 1571 in einen Befehl unten, wenn sie es nicht sind erforderlich, geben wir einfach einen leeren String zurück (was einem leeren Wert/keine Einstellung entspricht).

/release.js

try {
  execSync(
    `npm version ${version} --allow-same-version ${registry} && npm publish --access public ${force} ${registry}`
  );
} catch (exception) {
  setPackageJSONVersions(originalVersion, version);
}

if (process.env.NODE_ENV === 'development') {
  setPackageJSONVersions(originalVersion, version);
}

Nun zum lustigen Teil. Um eine Freigabe zu erstellen, müssen wir zwei Befehle ausführen:1589 und 1593 . Hier, 1601 ist verantwortlich für die Aktualisierung der Version unseres Pakets innerhalb von 1619 und 1622 führt die eigentliche Veröffentlichung des Pakets durch.

Für 1634 Beachten Sie, dass wir den inkrementierten 1641 übergeben wir haben mit 1651 generiert oben sowie 1668 Variable, die wir kurz vor dieser Zeile bestimmt haben. Dies weist NPM an, die Version auf diejenige festzulegen, die als 1678 übergeben wird und sicherzustellen, dass diese Version mit dem entsprechenden 1684 ausgeführt wird .

Als Nächstes rufen wir für die eigentliche Veröffentlichung 1695 auf Befehl, der den 1708 übergibt als 1713 kennzeichnen zusammen mit unserem 1727 und 1733 Flaggen. Hier der 1747 Teil stellt sicher, dass Pakete mit einem Scoped Namen öffentlich zugänglich gemacht werden (standardmäßig sind diese Pakettypen privat).

Ein bereichsbezogenes Paket ist eines, dessen Name in etwa so aussieht wie 1751 wo der 1763 Teil ist der "Umfang". Im Gegensatz dazu ist ein Paket ohne Bereich nur 1771 .

Um diesen Befehl auszuführen, beachten Sie, dass wir den 1780 verwenden Funktion importiert aus Node.js 1799 Paket (dies ist in Node.js integriert und muss nicht separat installiert werden).

Während dies technisch gesehen unsere Veröffentlichung erledigt, müssen noch zwei weitere Zeilen aufgerufen werden. Beachten Sie zunächst, dass wir unseren 1803 ausgeführt haben Rufen Sie 1812 an Block. Dies liegt daran, dass wir potenzielle Fehler bei der Veröffentlichung unseres Pakets vorhersehen müssen. Insbesondere möchten wir sicherstellen, dass wir nicht versehentlich eine neue Version, die noch nicht veröffentlicht wurde (aufgrund eines Skriptfehlers), in unserem 1827 belassen Datei.

Um dies zu verwalten, haben wir oben eine Funktion namens 1833 hinzugefügt die den 1846 aufnimmt und neu 1857 wir zuvor im Skript erstellt haben. Wir nennen das im 1866 Block unseres Codes hier, um sicherzustellen, dass die Versionen im Falle eines Fehlers sauber gehalten werden.

/release.js

const setPackageJSONVersions = (originalVersion, version) => {
  packageJSON.version = originalVersion;
  packageJSON.developmentVersion = version;
  fs.writeFileSync('package.json', JSON.stringify(packageJSON, null, 2));
};

Diese Funktion nimmt den 1878 Wert, den wir zuvor abgerufen und in dieser Variablen gespeichert haben, und ändert seinen 1881 und 1892 Felder. Wenn wir genau hinsehen, stellen wir sicher, dass 1906 eingestellt ist Feld zurück zum 1916 und das 1928 zum neuen 1932 .

Dies ist beabsichtigt. Wenn wir 1949 ausführen in dem Befehl, den wir an 1954 übergeben haben , egal was passiert, NPM wird versuchen, 1967 zu inkrementieren Feld in unserem 1974 Datei. Dies ist problematisch, da wir dies nur tun möchten, wenn wir versuchen, eine tatsächliche Leistung zu erbringen Produktionsfreigabe. Dieser Code mildert dieses Problem, indem er alle Änderungen überschreibt, die NPM vornimmt (was wir als Unfall betrachten würden), und sicherstellt, dass unsere Versionen synchron bleiben.

Wenn wir in unserem Release-Skript ganz unten nach unten schauen, rufen wir diese Funktion erneut auf, wenn 1985 , mit der Absicht, das geänderte 1999 zu überschreiben Feld auf die ursprüngliche/aktuelle Version zurücksetzen und 2000 aktualisieren auf die neue Version.

Fast fertig! Jetzt, da unser Veröffentlichungsskript fertig ist, müssen wir eine letzte Ergänzung zu unserem 2018 vornehmen Datei:

/Paket.json

{
  "type": "module",
  "name": "@cheatcodetuts/calculator",
  "version": "0.4.0",
  "developmentVersion": "0.7.0",
  "description": "",
  "main": "./dist/index.js",
  "scripts": {
    "build": "./node_modules/.bin/esbuild ./src/index.js --format=esm --bundle --outfile=./dist/index.js --platform=node --target=node16.3 --minify",
    "release:development": "export NODE_ENV=development && npm run build && node ./release.js",
    "release:production": "export NODE_ENV=production && npm run build && node ./release.js",
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
    "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "esbuild": "^0.14.1",
    "jest": "^27.4.3",
    "jest-cli": "^27.4.3",
    "semver": "^7.3.5"
  }
}

Hier möchten wir zwei neue 2024 hinzufügen :2033 und 2043 . Die Namen sollten hier ziemlich offensichtlich sein. Ein Skript soll eine neue Version unseres in Entwicklung befindlichen Pakets (für Verdaccio) veröffentlichen, während das andere für die Veröffentlichung in der NPM-Hauptregistrierung vorgesehen ist.

Das Skript besteht aus drei Teilen:

  1. Zunächst stellt es sicher, dass der passende Wert für 2053 eingestellt ist (entweder 2064 oder 2071 ).
  2. Führt einen neuen Build unseres Pakets über 2080 aus Rufen Sie unsere 2091 an Skript oben.
  3. Führt unser Freigabeskript mit 2107 aus .

Das ist es. Wenn wir jetzt entweder 2112 ausführen oder 2129 , stellen wir die entsprechende Umgebung ein, erstellen unseren Code und veröffentlichen unser Paket.

Lokales Testen mit Verdaccio und Joystick

Nun, um all dies zu testen, sind wir endlich Ich werde Verdaccio lokal einrichten lassen. Die gute Nachricht:Wir müssen nur ein Paket installieren und dann den Server starten; das ist es.

Terminal

npm install -g verdaccio

Hier verwenden wir 2132 Beachten Sie jedoch, dass wir den 2143 verwenden -Flag, was bedeutet, Verdaccio global zu installieren auf unserem Computer, nicht nur innerhalb unseres Projekts (absichtlich, da wir Verdaccio von überall ausführen können möchten).

Terminal

verdaccio

Einmal installiert, müssen wir zum Ausführen nur 2154 eingeben in unser Terminal und führen Sie es aus. Nach ein paar Sekunden sollten Sie eine Ausgabe wie diese sehen:

$ verdaccio
warn --- config file  - /Users/rglover/.config/verdaccio/config.yaml
warn --- Plugin successfully loaded: verdaccio-htpasswd
warn --- Plugin successfully loaded: verdaccio-audit
warn --- http address - http://localhost:4873/ - verdaccio/5.2.0

Wenn das läuft, können wir jetzt eine Testversion unseres Pakets ausführen. Zurück im Stammverzeichnis des Paketordners versuchen wir Folgendes auszuführen:

Terminal

npm run release:development

Wenn alles gut geht, sollten Sie eine ähnliche Ausgabe wie diese sehen (Ihre Versionsnummer lautet 2162 :

> @cheatcodetuts/[email protected] build
> ./node_modules/.bin/esbuild ./src/index.js --format=esm --bundle --outfile=./dist/index.js --platform=node --target=node16.3 --minify

  dist/index.js  600b

⚡ Done in 19ms
npm WARN using --force Recommended protections disabled.
npm notice
npm notice 📦  @cheatcodetuts/[email protected]
npm notice === Tarball Contents ===
npm notice 50B   README.md
npm notice 600B  dist/index.js
npm notice 873B  package.json
npm notice 1.2kB release.js
npm notice 781B  src/index.js
npm notice 1.6kB src/index.test.js
npm notice === Tarball Details ===
npm notice name:          @cheatcodetuts/calculator
npm notice version:       0.8.0
npm notice filename:      @cheatcodetuts/calculator-0.8.0.tgz
npm notice package size:  1.6 kB
npm notice unpacked size: 5.1 kB
npm notice shasum:        87560b899dc68b70c129f9dfd4904b407cb0a635
npm notice integrity:     sha512-VAlFAxkb53kt2[...]EqCULQ77OOt0w==
npm notice total files:   6
npm notice

Um jetzt zu überprüfen, ob unser Paket für Verdaccio freigegeben wurde, können wir unseren Browser für 2177 öffnen und sehen Sie, ob unser Paket erscheint:

Obwohl es großartig ist, dass dies funktioniert hat, möchten wir dieses Paket jetzt schnell in einer echten App testen.

Testen des Pakets in der Entwicklung

Um unser Paket zu testen, werden wir das Joystick-Framework von CheatCode nutzen, um uns dabei zu helfen, schnell eine App zu entwickeln, mit der wir testen können. Um es zu installieren, führen Sie in Ihrem Terminal Folgendes aus:

Terminal

npm install -g @joystick.js/cli

Und sobald es installiert ist, führen Sie von außerhalb Ihres Paketverzeichnisses aus:

Terminal

joystick create package-test

Nach ein paar Sekunden sehen Sie eine Nachricht von Joystick, die Sie zu 2183 auffordert in 2198 und führen Sie 2205 aus . Bevor Sie 2215 ausführen Lassen Sie uns unser Paket in dem Ordner installieren, der für uns erstellt wurde:

Terminal

cd package-test && npm install @cheatcodetuts/calculator --registry http://localhost:4873

Hier 2222 in unseren Test-App-Ordner und führen Sie 2236 aus Geben Sie den Namen unseres Pakets gefolgt von einem 2244 an Flag gesetzt auf die URL für unseren Verdaccio-Server 2253 . Dies weist NPM an, unter dieser URL nach dem angegebenen Paket zu suchen . Verlassen wir die 2265 Teil hier heraus, wird NPM versuchen, das Paket aus seiner Hauptregistrierung zu installieren.

Sobald Ihr Paket installiert ist, fahren Sie fort und starten Sie Joystick:

Terminal

joystick start

Öffnen Sie als Nächstes 2277 Ordner in einer IDE (z. B. VSCode) und navigieren Sie dann zum 2284 Datei, die für Sie im Stammverzeichnis dieses Ordners generiert wird:

/index.server.js

import node from "@joystick.js/node";
import calculator from "@cheatcodetuts/calculator";
import api from "./api";

node.app({
  api,
  routes: {
    "/": (req, res) => {
      res.status(200).send(`${calculator.divide(51, 5)}`);
    },
    "*": (req, res) => {
      res.render("ui/pages/error/index.js", {
        layout: "ui/layouts/app/index.js",
        props: {
          statusCode: 404,
        },
      });
    },
  },
});

Oben in dieser Datei möchten wir den Standardexport aus unserem Paket importieren (im Beispiel die Datei 2294 Objekt, das wir an 2301 übergeben haben in unserem Paketcode).

Zum Testen haben wir das Beispiel 2319 "gekapert". Route in unserer Demo-App. Dort verwenden wir den in Joystick integrierten Express.js-Server, um zu sagen:„Gib einen Statuscode von 200 und eine Zeichenfolge zurück, die die Ergebnisse des Aufrufs von 2324 enthält ." Angenommen, dies funktioniert, wenn wir unseren Webbrowser öffnen, sollten wir die Nummer 2338 sehen im Browser ausgedruckt:

Genial! Wenn wir dies sehen können, bedeutet dies, dass unser Paket funktioniert, da wir es ohne Probleme in unsere App importieren und seine Funktionalität aufrufen konnten (das beabsichtigte Ergebnis erhalten).

Freigabe für die Produktion

Okay. Zeit für den großen Abschluss. Nachdem all dies abgeschlossen ist, können wir unser Paket endlich über NPM der Öffentlichkeit zugänglich machen. Stellen Sie ganz schnell sicher, dass Sie ein Konto bei NPM eingerichtet und sich mit 2341 auf Ihrem Computer bei diesem Konto angemeldet haben Methode:

Terminal

npm login

Danach die gute Nachricht:Es ist nur ein einziger Befehl, um es zu erledigen. Aus dem Stamm unseres Paketordners:

Terminal

npm run release:production

Identisch mit dem, was wir bei unserem Aufruf von 2354 gesehen haben , sollten wir nach ein paar Sekunden eine Ausgabe wie diese sehen:

$ npm run release:production

> @cheatcodetuts/[email protected] release:production
> export NODE_ENV=production && npm run build && node ./release.js


> @cheatcodetuts/[email protected] build
> ./node_modules/.bin/esbuild ./src/index.js --format=esm --bundle --outfile=./dist/index.js --platform=node --target=node16.3 --minify

  dist/index.js  600b

⚡ Done in 1ms
npm notice
npm notice 📦  @cheatcodetuts/[email protected]
npm notice === Tarball Contents ===
npm notice 50B   README.md
npm notice 600B  dist/index.js
npm notice 873B  package.json
npm notice 1.2kB release.js
npm notice 781B  src/index.js
npm notice 1.6kB src/index.test.js
npm notice === Tarball Details ===
npm notice name:          @cheatcodetuts/calculator
npm notice version:       0.5.0
npm notice filename:      @cheatcodetuts/calculator-0.5.0.tgz
npm notice package size:  1.6 kB
npm notice unpacked size: 5.1 kB
npm notice shasum:        581fd5027d117b5e8b2591db68359b08317cd0ab
npm notice integrity:     sha512-erjv0/VftzU0t[...]wJoogfLORyHZA==
npm notice total files:   6
npm notice

Das ist es! Wenn wir zu NPM gehen, sollte unser Paket veröffentlicht werden (faire Warnung, NPM hat einen aggressiven Cache, daher müssen Sie möglicherweise ein paar Mal aktualisieren, bevor er angezeigt wird):

Alles erledigt. Herzlichen Glückwunsch!

Abschluss

In diesem Tutorial haben wir gelernt, wie man ein NPM-Paket mit Node.js und JavaScript schreibt. Wir haben gelernt, unseren Paketcode zu schreiben, Tests dafür mit Jest zu schreiben und ihn für eine Produktionsversion mit 2360 zu erstellen . Schließlich haben wir gelernt, wie man ein Release-Skript schreibt, das uns dabei geholfen hat, sowohl in einem lokalen Paket-Repository (mit Verdaccio) als auch im Haupt-NPM-Repository zu veröffentlichen.