Creating a UI Library & Github Actions & Rules, Tests and Version Management

Creating a UI Library & Github Actions & Rules, Tests and Version Management

Son 1 aydır npm paketleri ve buradaki yönetim ve best practiselerle alakalı okumalar ve denemeler yapıyordum. Hatta bunları yaparken bir tanede kendime ait icomoon-generator adında bir libraryde geliştirdim. Bu librarynin ne iş yaptığına niye oluşturduğuma daha sonra değineceğim. Fakat şimdi gördüğüm, öğrendiğim ve çalıştığım bu şeyleri uygulanan best practiselerle birlikte burada size anlatmaya çalışacağım.

What will we see?

1. Create a Github Repo
2. Creating the Project
3. Storybook
4. Prettier
5. Eslint
6. Button Components with Typescript
7. styled-components
8. prop-types
9. API Design
10. Composition API
11. Tests (jest Settings)
12. Tests (@testing-library/react Settings)
13. Tests
14. Module Formats
15. Typescript Declarations
16. husky
17. semantic-release
18. Github Environment Variable
19. Github Actions

Get Started

Step 1: Create a Github Repo

Github üzerinde ui-library adında bir repository oluşturdum. Ve oluştururken .gitignore ve Readme.md dosyalarınıda eklemesini istiyorum.

Step 2: Creating the Project

npx create-react-app ui-library — template typescript

Yukarıda ki kodu çalıştırarak create-react-app ile typescriptli bir react projesi oluşturuyoruz.

Sonra oluşturduğumuz github reposu ile bu oluşturduğumuz projeyi senkronize ediyoruz. Sonra da aşağıdaki gibi package.json dosyamızı kendimize ve oluşturduğumuz repositorye göre bilgilerini düzenliyoruz.

Daha sonra root dizinimize aşağıda ki “tsconfig.json” dosyasını ekliyoruz.

Sonra aşağıda ki gibi file-structure oluşturduktan sonra,

“Button” componentımı oluşturuyorum.

/root/src/ui/Button.tsx

/root/src/ui/index.ts

/root/src/index.ts

Step 3: Storybook

Ve şimdi de Storybook entegrasyonumuzu yapacağız. Bunun içni tek yapmamız gereken aşağıda ki kodu çalıştırarak projemize storybook’u entegre etmek.

npx sb init

Kurulumla birlikte gelen “stories” klasörünü proje dışındaki bir dizine taşıyın veya silin. Taşırsanız daha sonra bu örneklere bakıp kopyalar çekebilirsiniz veya direk silip documentationlarlada ilerleyebilirsiniz.

Sonra “stories” klasorüne gelip “introduction.stories.mdx” adında dosya oluşturuyoruz ve aşağıdakileri içine yapıştırıyoruz.

import { Meta, Description } from ‘@storybook/addon-docs/blocks’;
import README from ‘../../README.md’;

{README}

Ve daha sonra “Button.stories.tsx” story dosyasını oluşturup aşağıda ki kod blogunu ekliyoruz.

Bu yüklemelerden sonra hata alırsanız .storybook dosyasındaki main.js e gelip aşağıda ki typescript blogunu ekleyin.

Step 4: Prettier

İlk önce prettieri aşağıdaki gibi yüklüyoruz.

yarn add -D prettier

Ve .prettierrc.json adında dosya oluşturup aşağıdaki bir tane ayarı ekliyorum. Siz daha fazla prettier ayarı burada projeniz için ayarlayabilirsiniz tabi ki de.

Ve package.jsona gelip aşağıda ki 2 tane scripti ekliyoruz. Bunlar;

- format, prettiere göre dosyaların formatlanmasını düzeltilmesini sağlıyoruz.
- lint:format, herşeyin bu formatta olduğunun teyitini yapıyoruz.

Step 5: Eslint

Eslint ilede javascript yazım kurallarını set edeceğiz. Aşağıdaki paketleri öncelikle yüklüyoruz.

npm install — save-dev eslint eslint-config-prettier eslint-plugin-jest eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser

or

yarn add -D eslint eslint-config-prettier eslint-plugin-jest eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser

Sonra “.eslintrc” adında bir dosya oluşturup içine aşağıdakileri yapıştırıyoruz.

Bu config dosyası;

-root, config dosyasının, root config dosyası olduğunu söyler.
-parser, typescriptli eslint rulelarını parse etmesini söyler.
-recommend edilen eslint ayarları tanımlanır.
-react ve react hookslarının eslint recommended kuralları set edilir.
-prettier için eslint ayarlarını set eder.

Bu eslint kuralları typescriptlerle componentlarımızı tanımlamamızı zorunlu tutacağı için, Button componentımı aşağıda ki gibi düzenliyorum.

Aşağıda da “lint” adında yeni bir script tanımlarız. Böylece eslint ile ayarladığımız ayarlarla yazdığımız javascript kolarının kurallara uyup uymadığınız denetleriz.

“npm run lint” diyerek çalıştırmayı unutmayın :)

Step 6: Button Components with Typescript

Şimdi de kütüphanemizin ilk gerçek parçası Button componentını gerçek olarak oluşturalım.

Button componentımızı React.forwardRef ile sarıyoruz ve generator olarak HTMLButtonElement veriyoruz ve bu buradaki elementin bir HTMLButtonElementi olacağını söylüyor ve 2. Argument olarak da bu generatore propslarin alması gereken attribute kuralları vererek propsları sınırlıyoruz ve onu da React.ComponentsPropsWithoutRef ile <’button’> olmasını söylüyoruz.

Step 7: styled-components

Componentlarımızı oluşturmak içinde styled-components kütüphanesinden faydalanacağımız için aşağıdaki kodla öncelikle yüklüyoruz.

yarn add -D styled-components @types/styled-components polished
or
npm install --save-dev styled-components @types/styled-components polished

Sonra src altında utils adında klasör oluşturup içinde de, colors.ts veya styles.ts adında dosyalarımızı oluşturup, aşağıda ki değişkenleri tanımlıyoruz.

Daha sonra Button.tsx klasörüne gelip yukarıda ki style.ts dosyasını tanımlıyoruz ve bu butonu export ediyoruz ve Button.tsx içinde kullanıyoruz.

Daha sonra Button.tsx dosyamız da style.ts den gelen styled-component ile oluşturduğum Button değişkenini component gibi kullanıyorum ve defaultProps tanımlıyorum.

Step 8: prop-types

Prop kontrollerini aslında typescript ile zaten yapıyoruz ama birde ekstra bir kütüphane ile de yapabiliyoruz. Biz bunu da ekstradan propsları kurallandırmak, sınırlandırmak için kullanacağız. Önce aşağıdaki gibi modülü yüklüyoruz.

npm install --save-dev prop-types
or
yarn add -D prop-types

Step 9: API Design

UI librarylerinde 2 farklı API design, tasarım şekli vardır. Birisi Traditional API yani Geleneksel API, bir diğeri Compound API yani Bileşik API. Bunlar arasında ki farklar aşağıda ki gibidir.

Step 10: Composition API

Yeni nesil UI libraryleri genellikle bu yöntemi kullanmayı tercih ederler. Bu yöntem hem daha kontrol edilebilir hem de daha esnektir kullanım açısından.

İlk önce src altında “Field” adında bir klasör ve içinde bir style.ts dosyası oluşturup aşağıda ki kodları içinde tanımlıyoruz.

Daha sonra bu “Field” klasörü içinde “FieldContext” adın da bir context tutacağız. Burada createContext hooksu ile context oluşturup form alanlarında bu contextProviderını kullanacağız.

Ve ayrıca bir altta ki “Field” componentımızda kullanacağımız unique id utilsini, utils klasörü altında useUniqueID.ts dosyası içinde aşağıdaki gibi oluşturuyoruz. Tabi önce bunu yapmamızı sağlayacak olan nanoid librarysini yüklüyoruz.

npm install --save-dev nanoid
or
yard add -D nanoid

Daha sonra yukarıda ki “ContextProvider”ı “Input”, “Label” gibi alanlari içine koyacağımız bir “Field.tsx” tanımlayacağız.

Daha sonra “Input” ve “Label” componentlarımızı oluşturacağız.

Daha sonra “Input.stories.tsx” adında yeni bir story dosyası oluşturup asağıda ki gibi “Field” ve içine gömdüğümüz “Input” ve “Label”ı çağırarak burada kullanabiliyor olacağız.

Ve son olarak da gelin yine Field klasörü altında bir diğer form elemanımız Textarea componentimizi oluşturalım.

Field.tsx dosyamızda da gerekli alanlara aşağdıa ki gibi Textarea’ı ekliyoruz.

Ve son olarak da Textarea.stories.tsx dosyasını oluşturup aşağıda ki kodları gönderiyoruz.

Ve artık Button ve Form bileşenlerine sahip bir UI kütüphanemiz hazır diyebiliriz :)

Step 11: Tests (Jest Settings)

İlk önce gerekli kurulumları aşağıda ki gibi yapıyoruz.

npm install --save-dev jest ts-jest @types/jest react-is
or
yarn add -D jest ts-jest @types/jest react-is

Daha sonra packaga.json içinde test adında bir script oluşturacağız aşağıda ki gibi. Şuana kadar olan tüm scriptlerimizde bu kadar zaten.

Daha sonra root dizininde jest.config.js adıdna dosya oluşturup aşağıda ki ayarları içine yazıyoruz.

Step 12: Tests (@testing-library/react Settings)

Yine aşağıda ki kurulumları ilk olarak gerçekleştiriyoruz.

npm install --save-dev @testing-library/react @testing-library/jest-dom or
yarn add -D @testing-library/react @testing-library/jest-dom

Daha sonra “jest-setup.ts” adında dosya oluşturup aşağıda ki 2 importu içine gönderiyoruz. Bunlarda react testleri için styled-component uyumluluğunu ve react için jest-dom’u aktif ediyor bizim için.

tsconfig.json içine de eğer ekli değilse aşağıdaki satırı ekliyoruz.
“include”: [“src/**/*”, “./jest-setup.ts”]

Step 13: Tests

Daha sonra yeri size kalmış ama ben Button component klasörü içinde Button.spec.ts adında bir dosya oluşturup Button componentimizin testlerini yapıyoruz.

Field testleri için useUniqueID adında bir mock utilse ihtiyacım olacağı için aşağıda ki gibi utils klasörü altında, __mocks__ klasörü oluşturup içinde “useUniqueID.ts” adında dosya oluşturup aşağıdaki mock utili oluşturuyorum.

/root/src/utils/__mocks__/useUniqueID.ts

Daha sonrada “Field.spec.ts” adında test dosyamızı oluşturup aşağıda ki testleri yazıyoruz.

Step 14: Module Formats

Bu aşamada ise oluşturduğumuz ui librarymizi babel ve @rollup librarylerini kullanıp compressleyip dist çıktısını alacağız.

Önce aşağıdaki gerekli libraryleri kuruyoruz.

npm install @babel/runtime
npm install --save-dev @babel/preset-env @babel/preset-typescript @babel/preset-react @babel/plugin-transform-runtime babel-plugin-styled-components
or
yarn add @babel/runtime
yarn add -D @babel/preset-env @babel/preset-typescript @babel/preset-react @babel/plugin-transform-runtime babel-plugin-styled-components

Şimdide .babelrc dosyası oluşturup aşağıda ki kodları yapıştırıyoruz.

Ve şimdide rollup paketlerini yüklüyoruz. Bunlar build timeda kullanılacak paketler olduğu için devDependencies olarak ekleyebiliriz.

npm install --save-dev rollup rollup-plugin-delete rollup-plugin-node-externals @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve
or
yarn add -D rollup rollup-plugin-delete rollup-plugin-node-externals @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve

Ve rollup.config.js dosyası oluşturup aşağıda ki ayarları yerleştiriyoruz.

Step 15: Typescript Declarations

Daha sonra tsconfig.build.json ayarlarımızı aşağıda ki gibi yapıyoruz.

Bu tanımlamalarla da index.d.ts gibi typescript rulelarını set eden dosyaları build olarak dist çıktı klasörümüze kopya olarak alacağız.

Daha sonra “/root/src” altında “index.ts” dosyası oluşturup bundle edilecek tüm dosyaları burada export ediyoruz.

Daha sonra package.json dosyamıza aşağıdaki “main”, “module” ve “sideEffects” kısımlarınıda ekliyoruz ve scriptlerimizin son halide aşağıda ki gibidir.

Ve daha sonra aşağıda ki kodları sırayla çalıştırıp kontrol edip buildi aldığımızı görüyoruz.

npm run format
npm run format:lint
npm run build

Step 16: husky

Husky ile ise githuba commit göndermeden veya pushlamadan önceki çalışması gereken actionları komut olarak vereceğiz. Örneğin her commit önceki eslint checki yapmasını istiyorum ve eğer eslint hatası varsa commiti kabul etmesini istemiyorum. İlk önce eslint hatalarını giderdikten sonra tekrar gelip commit atmasını ve tekrar denetleyerek hata yoksa commitini göndermesini istiyorum gibi.

İlk önce aşağıda ki kodla “husky”i yüklüyoruz.

npm install husky --save-dev
or
yarn add -D husky

Ve daha sorna aşağıda ki kodla git hookslarını aktif ediyoruz.
npx husky install

Kurulumdan sonra git hookslarını otomatik etkinleştirmek içinde aşağıda ki kodu set ediyoruz.
npm set-scrpit prepare “husky install”

Ve şimdi de pre-commit ile commitlerden önce çalıştırılması gereken scriptleri set ediyoruz aşağıda ki gibi.
npx husky add .husky/pre-commit “npm run lint && npm run format:lint && npm run test”

Ve şimdide push önceki olan pre-push daki kısmı ayarlıyoruz.

npx husky add .husky/pre-push “npm run lint && npm run format:lint && npm run test”

Ve bu kadar şimdi değişiklikleri tekrar githuba pushlayabiliriz.

Step 17: semantic-release

Ve artık son adımlarımızdan birisi olan semantic bir şekilde release çıkmaya geldik diyebiliriz. Burada yapacağımız ayarlarla semantic olarak version ve tag alabileceğiz, change log kayıtlarımız otomatik tutulacak ve commit mesajlarımızı daha düzgün bir şekilde tutabileceğiz.

İlk önce aşağıda her zaman yaptığımız gibi gerekli libraryleri yüklüyoruz.

yarn add -D semantic-release [@semantic](twitter.com/semantic "Twitter profile for @semantic")-release/changelog [@semantic](twitter.com/semantic "Twitter profile for @semantic")-release/git commitizen cz-conventional-changelog
or
npm install --save-dev semantic-release [@semantic](twitter.com/semantic "Twitter profile for @semantic")-release/changelog [@semantic](twitter.com/semantic "Twitter profile for @semantic")-release/git commitizen cz-conventional-changelog

Sonra aşağıda ki release, repository alanını ve yeni 2 scriptimizi package.jsona ekleyelim.

Artık commitlerimizi git add yaptıktan sorna npm run commit diyerek çalıştırdıktan sonra commitizen packagenin standartlarına göre göndereceğiz. Ve pushladıktan sonra github actionslar semantic-release paketiyle birlikte automation işlemini otomatik olarak yapacak.

Step 18: Github Environment Variable

Önce Github actionslarında kullanılmak üzere ilk başta NPM_TOKEN adında bir envıronment variable tanımlamamız gerekiyor github repomuza. Bu tokeni almak için npme gidip bir tane auyomation için token oluşturuyoruz ve aşağıdaki gibi oluşturduğumuz ui librarysinin github reposunun ayarlarında ki Secret kısmında, NPM_TOKEN name olacak şekilde npmden aldığımız token ile burada yeni bir environment variable tanımlıyoruz.

Github-actionslara geçmeden önce son olarakda ui-library olan package ismimizi unique bir npm package ismi seçerek değiştiriyoruz. Npm kütüphanesine girerek seçtiğiniz paket ismini kontrol edin. Ve privateyide false yapıyoruz çünkü free npm üyeliğiyle private npm packageleri yükleyemiyoruz. Bizimkisi de free olduğu için public bir npm kütüphanesi oluşturuyoruz.

Step 19: Github Actions

Ve şimdi tüm ayarlarımızı yaptığımıza göre sıra oluşturduğumuz libraryi npme publish etmeye geldi. Npm publish işini github actionslarla otomatik bir şekilde yapacağız. Süreç şu şekilde işleyecek. Biz bir commit gönderdiğimiz de yani githuba push ettiğimiz de githubda aşağıda yazdığımız ci actionu çalışıp npm release ve semantic versioning automationunu bizim için yapacak.

Bunun için tek yapmamız gereken aşağıdaki dizine release veya istediginiz bir dosya adı .yml şeklinde bir dosya oluşturmak ve aşağdıa ki kodları oraya yazmak. Yorumlu satırdaki test kısmını bilerek bıraktım isterseniz oradaki gibi ikinci bir job daha ekleyip kullanabilirsiniz.
/root/.github/workflows/release.yml