W repozytorium npm dostępnych jest całe mnóstwo re-używalnych komponentów ReactJS. Pod tym względem “ekosystem” Reacta jest bardzo rozwinięty. Pracując w tej technologii na codzień, sami tworzymy mnóstwo przydatnych komponentów. Dlaczego by więc nie opublikować swoich rozwiązań w tym repozytorium, tak aby każdy mógł wykorzystać naszą pracę? Dzięki temu każdy z nas może stać się kontrybutorem open-soure! Brzmi kusząco prawda? Sam to swego czasu zrobiłem - tutaj możecie podziwiać moje “dzieło”. Dzięki temu, że przetarłem już ten szlak, mogę teraz podzielić się swoimi doświadczeniami… I stąd właśnie wziął pomysł na dzisiejszy wpis - przedstawię Ci jak w praktyce wygląda publikacja komponentu ReactJS w npm!

P.S. Ostatnio na “fejsie” ktoś się przyczepił, że bezczelnie zrzynam teksty z innego bloga… Oczywiście chodziło o Front-End Insights czyli mojego własnego bloga anglojęzycznego. Myślę, że nie jest żadną tajemnicą, że część tekstów z tamtego bloga pojawia się, oczywiście przetłumaczona, tutaj (tłumaczenia w drugą stronę też są możliwe, choć na razie projekt anglojęzyczny leży odłogiem). Tak jest i w tym przypadku więc mam nadzieję, że tym razem nikt się nie przyczepi… Takich wpisów (tłumaczonych) będzie w przyszłości coraz mniej ponieważ, na tę chwilę blog po polsku jest dla mnie priorytetem!

Tworzenie projektu dla komponentu

Przy tworzeniu komponentu ReactJS w celu jego późniejszej publikacji w npm, pierwszym problem przed jakim staniesz jest to, jaką strukturę powinien mieć projekt zawierający ten komponent. Wątpliwości budzić może również to, co powinno znaleźć się na wyjściu takiego projektu. Tutaj chodzi mi głównie o to, jaki format powinien przyjąć wynikowy kod, tak aby był on łatwo używalny w innych projektach.

Co do drugiej z wątpliwości to w sumie wiadomo - rezultatem naszego projektu ma być komponent ReactJS. Kod tego komponentu powinien przyjąć postać ES5. Dzięki temu będzie on mógł być wykorzystany zarówno w projektach napisanych z użyciem ES5 jak i ES6. Jednocześnie w projekcie komponentu wolę użyć ES6 aby móc wykorzystać wszystkie jego zalety. Z tego też powodu myślę, że wykorzystanie webpacka i babela nasuwa się jako “oczywista oczywistość”.

Kolejna sprawa to testowanie. Jest to bardzo istotne jeśli chcesz swój komponent opublikować w npm. W końcu miliardy programistów z całego świata może potencjalnie chcieć użyć Twojego super komponentu w swoich projektach…

A może generator?

Co więc zrobiłem by rozwiać swoje wątpliwości? Jak zwykle przed rozpoczęciem nowego projektu, zanim zacznę coś samodzielnie dziergać, sprawdzam czy dla mojego problemu dostępne są jakieś generatory Yeomana. I nie zgadniesz… oczywiście, że są! Dzięki temu, aby zacząć projekt wystarczy, że zrobię to:

npm install -g generator-react-webpack-component
yo react-webpack-component

Kod wygenerowany przez ten generator jest przygotowany pod tworzenie komponentów posiadających jakiś widok. Stąd też obecność skryptów npm, które uruchamiają webpack-dev-server. Miło ze strony tego generatora - może Ci się to przydać kiedy będziesz tworzyć tego typu komponenty.

Niestety, generator ten jest już trochę stary i brakuje mu pewnych paczek npm. Aby to naprawić wystarczy je zainstalować:

npm install --save-dev node-sass phantomjs

Struktura projektu

Spójrz teraz na strukturę projektu, który się wygenerował. Możesz w nim znaleźć katalog lib. To jest właśnie miejsce, w którym powinieneś umieścić swój komponent. Na razie znajduje się tam komponent przykładowy, który możesz usunąć lub zmodyfikować. Oprócz przykładowego komponentu, w katalogu lib znajduje się też plik index.js. Jest to punkt wejściowy naszego projektu - spójrzmy na jego zawartość:

module.exports = require('./react-component-npm.jsx');

Jak widzisz, mamy tutaj definicję modułu w stylu node.js - nic nie stoi na przeszkodzie aby przerobić to na ES6. Moduł ten importuje komponent znajdujący się w katalogu lib. Spójrzmy teraz jak on wygląda:

import React from 'react';
import './react-component-npm.scss';

export default React.createClass({
  render: function() {
    return <div className="react-component-npm">Hello World</div>;
  }
});

Tutaj mamy z kolei jakiś dziwny miks ES6 (importy) i definicję klasy ReactJS w stylu ES5. Oczywiście tutaj również warto przerobić kod na ES6.

Testowanie komponentu

Ok, skoro mamy już gotową strukturę projektu, warto jeszcze sprawdzić jakie mamy możliwości testowania naszego komponentu. W zasadzie to widzę tutaj dwie opcje: testy jednostkowe oraz testowanie komponentu jako pakietu npm. Na początek rozważmy pierwszą opcję.

Testowanie za pomocą testów jednostkowych

Generator, którego użyłem do wygenerowania struktury projektu posiada wsparcie dla testów jednostkowych tworzonego komponentu, dostępne z pudełka! Wedle tej struktury, wszystkie testy jednostkowe powinny znaleźć się w katalogu spec. Jest już tam oczywiście przykładowy test, spójrzmy więc na niego:

import React from 'react/addons';
import ReactComponentNpm from '../lib/react-component-npm.jsx';

describe('ReactComponentNpm', function() {
  var component;

  beforeEach(function() {
    component = React.addons.TestUtils.renderIntoDocument(
      <ReactComponentNpm/>
    );
  });

  it('should render', function() {
    expect(component.getDOMNode().className).toEqual('react-component-npm');
  });
});

Jak widzisz, jest to bardzo prosty test sprawdzający czy komponent zawiera element DOM o określonej wartości atrybutu className. Myślę, że testowanie komponentów ReactJS to temat na osobny wpis, dlatego na tym dziś poprzestanę.

W omawianym projekcie, do testowania użyto narzędzi: Jasmine oraz Karma. Mamy też dostępne komendy, które pozwalają nam łatwo uruchamiać testy jednostkowe. Poniżej komenda uruchamiające testy jednorazowo:

npm run test

Jest też opcja uruchomienia testów, a następnie oczekiwania na zmiany (w przypadku wykrycia zmian w testowanym kodzie, testy uruchamiają się ponownie):

npm run watch-test

To w zasadzie wszystko co musisz wiedzieć, na temat testów jednostkowych zanim nastąpi publikacja komponentu ReactJS w npm. Myślę, że nie muszę nikogo przekonywać do tworzenia takich testów - skoro chcesz się podzielić swoim kodem z innymi, warto mieć pewność, że wszystko działa.

Testowanie komponentu jako pakietu npm

Zanim opublikujesz swój komponent w npm warto przetestować, jak przygotowany przez Ciebie pakiet zachowa się w jakimś Twoim lokalnym, przykładowym projekcie. Aby to zrobić, musimy na podstawie naszego projektu wygenerować testową paczkę npm.

Konfiguracja webpacka

W tym celu zmienimy co nieco konfigurację webpacka w naszym projekcie. Nie jest to absolutnie konieczne, ale lepiej to zrobić aby nasz pakiet spełniał standardy. Otwórzmy więc plik konfiguracyjny webpacka czyli webpack.config.js znajdujący się w głównym katalogu projektu. Jeśli spojrzysz na obiekt przypisany do właściwości output to zauważysz, że plik wynikowy ustawiony jest na wartość react-component-npm.js. Moim zdaniem dobrą praktyką jest, aby plik ten zawsze nazywał się index.js. Od razu wtedy wiadomo, że to punkt wejścia do naszej biblioteki. Druga rzecz do zmiany to ścieżka wyjściowa. Zmieńmy ją na /dist:

output: {
  path: path.join(__dirname, 'dist'),
  filename: 'index.js',
  libraryTarget: 'umd',
  library: 'ReactComponentNpm'
}

Jeśli chcesz, możesz też oczywiście zmienić nazwę swojej biblioteki na taką, która bardziej Ci odpowiada. Służy do tego właściwość library. Spróbujmy teraz zbudować nasz projekt:

npm run build

Po wykonaniu tego polecania, wszystko powinno się ładnie zbudować, a w głównym katalogu projektu powinien pojawić się katalog dist zawierający plik index.js.

Konfiguracja npm

Przejdźmy teraz do pliku package.json. Jak wiadomo znajduje się w nim konfiguracja projektu npm. To co trzeba tutaj zrobić, zanim opublikujemy nasz projekt w repozytorium npm, to zmiana opcji main, tak aby wskazywała ona na plik dist/index.js. Wynika to z tego, że przed momentem zmieniliśmy trochę konfigurację webpacka - ścieżka do pliku wyjściowego i jego nazwa zmieniły się.

Jeśli nie wiesz do czego służy opcja main pliku package.json to śpieszę z wyjaśnieniem! Otóż wskazuje ona na punkt wejścia naszej biblioteki. Gdy ktoś zainstaluje nasz komponent z repozytorium npm, to przy imporcie to właśnie w tym pliku będzie szukał dostępnych modułów.

Publikacja lokalna

Ok, całą niezbędną konfigurację mamy już za sobą. Czas teraz przystąpić do wygenerowania lokalnie paczki z naszym komponentem. Umożliwi nam to zainstalowanie tego pakietu w innym projekcie i użycie naszego komponentu, w celu jego przetestowania.

Sama publikacja lokalna jest bardzo prosta - wystarczy wywołać poniższą komendę w głównym katalogu projektu:

npm pack

Wywołanie powyższej komendy spowoduje utworzenie pliku react-component-npm-1.0.0.tgz w głównym katalogu projektu.

Zwróć uwagę na nazwę tego pliku. Powstaje ona z połączenia wartości opcji name oraz version ustawionych w pliku package.json. Z tego wniosek, że jeśli chcesz zmienić nazwę swojego pakietu, to właśnie w tym pliku możesz to zrobić.

No dobra. Pakiet lokalny mamy już utworzony. Czas teraz zainstalować nasz pakiet w innym naszym lokalnym projekcie. Aby to zrobić, przejdź w konsoli do głównego katalogu tamtego projektu i wywołaj poniższą komendę:

npm install /path/to/your/package/react-component-npm-1.0.0.tgz

W tym momencie zadzieje się “magia” i pakiet zostanie zainstalowany. Możesz to sprawdzić zaglądając do katalogu node_modules swojego lokalnego, testowego projektu. Powinien się tam znajdować katalog o nazwie zgodnej z nazwą Twojego pakietu. Oprócz tego, w pliku package.json tego projektu, lista dependencies powinna zostać zaktualizowana o wpis z nazwą Twojego komponentu.

I to tyle - możesz teraz spróbować zaimportować stworzony przez siebie komponent i testowo go wykorzystać.

Eksport komponentu do npm

No dobra. Mamy już utworzony i dobrze przetestowany komponent. Ostatnia rzecz, która nam została to właściwa publikacja komponentu ReactJS w npm. W tym celu musimy wykonać kilka kroków…

Aby móc publikować w npm, musisz skonfigurować swoje dane autora. Wymaga to wywołania poniższych komend w konsoli:

npm set init.author.name "Jan Kowalski"
npm set init.author.email "jasiu@buziaczek.pl"
npm set init.author.url "http://janekoduje.com"

npm adduser

Ok. Teraz należy zainstalować globalnie pakiet pakmanager:

npm install -g pakmanager

Kiedy to zrobimy, możemy użyć tego narzędzia do sprawdzenia czy nie brakuje nam w projekcie żadnych zależności:

pakmanager deps

Jeśli narzędzie to wykaże jakieś brakujące pakiety, należy je zainstalować lub dodać je do sekcji dependencies w pliku package.json.

Ostatnia rzecz przed publikacją - dodaj plik .npmignore! Plik ten działa tak samo jak inne pliki tego typu (na przykład .gitignore). Wypisz w nim wszystko co nie powinno znaleźć się w wynikowym pakiecie npm. Najważniejsze, to żebyś w pliku tym nie umieszczał katalogu dist ani pliku package.json. One musza koniecznie znaleźć się w eksportowanym pakiecie. Inaczej pakiet nie będzie działać! Poniżej przykład takiego pliku, dla mojego komponentu, o którym wspominałem na początku wpisu:

# WebStorm
.idea

# dist
lib

# test
spec

# node
node_modules

# other files
.babelrc
.editorconfig
.eslintrc
.gitignore
.npmignore
index.html
karma.conf.js
webpack.config.js
pakmanaged.js

Pamiętaj też, aby dodać plik README.md do projektu Twojego komponentu i dobrze opisać w nim sposób jego użycia! To ułatwi, potencjalnym odbiorcom Twojej biblioteki, zorientowanie się jak działa Twój komponent oraz czy spełnia on ich wymagania i potrzeby. Zawartość tego pliku będzie, po publikacji, wyświetlała się na stronie Twojego pakietu dostępnej w serwisie nmpjs.org. Dlatego też nazwa tego pliku ten nie może być wyszczególniona w pliku .npmignore.

Alright! Czas wreszcie opublikować nasz komponent! Żeby to zrobić, wywołaj poniższą komendę z poziomu głównego katalogu projektu tego komponentu:

npm publish ./

I gotowe! Możesz teraz przejść na stronę npmjs.org i sprawdzić czy Twój pakiet się tam pojawił…

Publikacja komponentu ReactJS w npm - podsumowanie

Wydaje mi się, że publikacja komponentu ReactJS w npm jest dość prosta. Mam nadzieję, że powyższy poradnik będzie dla Ciebie w tym procesie pomocny! Myślę, że warto dzielić się ze społecznością dobrymi pomysłami. A zaistnienie jako autor pakietów npm może być całkiem przydatnym atutem, na przykład, na rynku pracy… Jak więc widzisz, może to być z pożytkiem zarówno dla Ciebie jak i dla innych.