ওয়েবপ্যাক দিয়ে রিঅ্যাক্ট এর স্টার্টার প্যাক — স্ক্র্যাচ থেকে

আমরা রিঅ্যাক্ট এ অ্যাপ্লিকেশন ডেভেলপমেন্ট করার আগে সাধারণত create-react-app CLI দিয়ে প্রথমের রিঅ্যাক্ট এর স্টার্টার প্যাক বানিয়ে ফেলি। অথবা অলরেডি এরকম বানানো আছে এমন প্যাক নামিয়ে সেটার উপর কাজ করা শুরু করি। বিগিনার লেভেল এর জন্যে এই পদ্ধতিটাই সবচেয়ে বেস্ট, কারণ এই প্যাক এর এনভারোমেন্ট সেটাপ করতে করতেই অনেকে ডিমোটিভেট হয়ে আসল রিঅ্যাক্ট শিখাই বাদ দিয়ে দিতে পারে। তবে ভয় পাওয়ার কারণ নাই, এটা খুব অ্যাডভান্স কিছু না, স্টেপ বাই স্টেপ করলে দেখবেন আসলেই অনেক সোজা। তাই যাদের একান্ত ইচ্ছা আছে বা আসলেই জানতে চান কিভাবে এই স্টার্টার প্যাকটা বানানো হয়, কি কি থাকে, কিভাবে কি কাজ করে, তাহলে তাদের জন্যেই আজকে আমার লেখা।

আমি সবকিছু একদম পার্ট পার্ট করে ব্যাখ্যা করে দিবো, এবং সবশেষে আমার বানানো প্যাকগুলোর লিঙ্কও পাবেন, যেগুলো ডাউনলোড করে আপনি আপনার মন মতো কাস্টমাইজ করেও কাজ করতে পারবেন। চাইলে লাইভের পোর্ট চ্যাঞ্জ করতে পারবেন, নতুন কোনো প্লাগিনও লাগাতে পারবেন আপনার কাজকে আরো অটোম্যাট করতে।

প্রথমেই আপনার ডিভাইসে অবশ্যই নোড জেএস ইন্সটল থাকতে হবে। নোড জেএস এবং NPM আছে কিনা দেখার জন্যে এদের ভার্শন চ্যাক করতে পারেন কমান্ড লাইন থেকে। বাই দ্যা ওয়ে, কমান্ড লাইন আপনার পছন্দমতো যেকোনো একটা ইউজ করলেই হবে। জাস্ট কাজ করলেই হবে।

Node JS এবং NPM এর ভার্শন চ্যাক করুনঃ

node --version 
বা,
node -v

এবং

npm --version
বা,
npm -v

ভার্শন দেখালে ঠিক আছে। অন্যকিছু দেখালে তাহলে হয়তো আপনাকে Node JS ইন্সটল করতে হবে।

যাই হউক এবার আমরা মেইন প্রোজেক্টে কাজ করবো। একদম স্ক্র্যাচ থেকে সবকিছু শুরু করবো। আপনার মনমতো যেকোনো জায়গায় একটা ডিরেক্টরি নিয়ে প্রোজেক্ট শুরু করুন।

mkdir react-starter-pack

এবার আপনার ডিরেক্টরিতে চলে যানঃ

cd react-starter-pack

এবার একটা NPM প্রোজেক্ট ইনিশিয়েট করুনঃ

npm init

এখানে আপনার পছন্দমতো ডিটেইলস দিয়ে সবকিছু পূরণ করুন। সবশেষে কনফার্ম করে ফেলুন। দেখবেন আপনার ডিরেক্টরিতে সব ঠিক থাকলে একটা এরকম package.json ফাইল তৈরী হয়েছেঃ

{
  "name": "react-starter-pack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Zonayed Ahmed",
  "license": "MIT"
}

এবার রিঅ্যাক্ট এ সাধারণত সব স্ক্রিপ্ট ফাইলগুলো বান্ডেল হয়ে প্রোডাকশন ভার্শনে একটা স্ক্রিপ্ট ফাইলে চলে যায়। যেই কাজটাই ওয়েবপ্যাক দিয়ে করা হয়। আবার একই সাথে ডেভেলপমেন্ট এর সময় আমাদের লাইভসার্ভারের কাজও লাগে যেটা লোকাহোস্টের একটা পোর্টে চলন্ত অবস্থায় থাকে। আমাদের বান্ডেল করার জন্যে webpack, লাইভ ডেভেলপমেন্ট সার্ভারের জন্যে webpack-dev-server এবং সবশেষে webpack কম্পাইলারের জন্যে webpack-cli প্যাকেজ লাগবে। এগুলো ডেভ ডিপেন্ডেসি হিসাবে ইন্সটল করে ফেলুনঃ

npm install --save-dev webpack webpack-dev-server webpack-cli

এখন ওয়েবপ্যাকের জন্যে একটা প্রোজেক্টের রুটে webpack.config.js ফাইল বানাতে হবে যেখানে ওয়েবপ্যাকের কনফিগারেশনগুলো থাকবেঃ

touch webpack.config.js

এখন ওয়েব ডেভেলপমেন্ট এ কনভেনশনালি প্রোজেক্টের স্ট্রাকচার অনেকটা দেখবেন একটা src ডিরেক্টরি থাকে যেখানে আপনার ডেভেলপমেন্ট এর সময়কার ফাইল/কোড গুলো থাকে, আর আরেকটা dist বা build নামে একটা ডিরেক্টরি থাকে যেখানে আপনার প্রোডাকশন ভার্শন থাকে। আমাদের এই প্যাকেও আমরা সেইম কনভেনশান মেনেই স্টারকচার করবো। তবে আপনি চাইলে আপনার মতো করেও যেকোনোভাবে করতে পারবেন।

একটা ডিরেক্টরি তৈরী করুন src নামে যেটার ভিতরে একটা স্ক্রিপ্ট ফাইল রাখুন index.js নামেঃ

mkdir src
touch src/index.js

এবার index.js er ফাইলের ভিতরে কিছু কোড লিখুন জাস্ট দেখার জন্যেঃ

console.log('Hello React Starter Pack!');

এবার আমরা এটাকে dist নামে একটা ফোল্ডারে নিয়ে বান্ডেল তৈরী করবো। এর জন্যে আমাদের webpack.config.js এ ওয়েবপ্যাকের জন্যে কিছু কনফিগারেশন করে দিতে হবে এরকমঃ

module.exports = {
  entry: './src/index.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  }
}

এখানে একটা অবজেক্ট ক্রিয়েট করে সেটা এক্সপোর্ট করা হচ্ছে। সাথে সেই অবজেক্ট এর ভিতরে এন্ট্রি পয়েন্ট দিতে হবে। এর নিচে আবার আউটপুট নামেরও আরেকটা অবজেক্ট নিতে হবে যেখানে আপনি আপনার পাথ মেনশন করে দিতে হবে যেখানে আপনি বান্ডেল ফাইলটা রাখতে চাচ্ছেন। কিন্তু এখানে পাথ এর লোকেশান একদম অ্যাবসিলিউট হতে হবে, তাই আপনারে __dirname ইউজ করে বাকি পাথটা রিলেটেভলি মেনশন করে দিতে হবে, অন্যথায় এরর আসবে। এবং দ্বিতীয়টায় আপনাকে বান্ডেল করা ফাইলের নামটাও মেনশন করে দিতে হবে। আমি এখানে আমার বর্তমার প্রোজেক্টের ডিরেক্টরিতে dist নামে একটা ডিরেক্টরির ভিতরে bundle.js নামে একটা ফাইলে বান্ডেলটা রাখতে চাচ্ছি। আপনি এখানে চাইলে যেকোনো নামই ইউজ করতে পারবেন।

এবার ওয়েবপ্যাকের ব্যাসিক কনফিগারেশন রেডী। এবার NPM দিয়ে এটা রান করাতে চাচ্ছি, তাই package.json এ কিছু scripts লিখতে হবে। package.json ওপেন করে scripts এর ভিতরে সবকিছু কেটে নতুন একটা build স্ক্রিপ্ট লিখুনঃ

...
"scripts": {
    "build": "webpack"
  },
...

এবার স্ক্রিপ্ট রেডি। কমান্ড লাইন থেকে build স্ক্রিপ্টটা রান করানঃ

npm run build

আর দেখুন একটা dist ডিরেক্টরিসহ ভিতরে bundle.js তৈরী হয়ে গেছে আপনার প্রোজেক্টে।

এখন এখানে build স্ক্রিপ্ট এ কিছু ফ্ল্যাগও দিতে পারবেন। যেমন আমরা সাধারণত প্রোডাকশন ভার্শনে মিনিমাইজড ভার্শনের বান্ডেলটাই রাখি। কম সাইজ, লোড তাড়াতাড়ি হবে। তাই এখানে কিছু ফ্ল্যাগ লাগিয়ে দিতে পারেন সে কাজটা করার জন্যেঃ

...
"scripts": {
    "build": "webpack --mode production"
  },
...

এবার এই স্ক্রিপ্ট রান করালে প্রোডাকশন ভার্শনের বান্ডেল পাবেন। নোটঃ কোনো mode না দিলে ওয়েবপ্যাক বাই ডিফল্ট প্রোডাকশন ভার্শনে বান্ডেল করে। কিন্তু এখানে একটা ওয়ার্নিং আসবে তাই মুড মেনশন করে দেওয়াই ভালো।

এবার আপনার অনেক প্রোজেক্টে দেখবেন একটা public নামে একটা ডিরেক্টরি থাকে যেখানে আপনার ব্যাসিক ফাইলগুলো যেমন index.html থাকে। এখন আমরা তাই একটা প্লাগিন html-webpack-plugin ইউজ করবো এই উদ্দেশ্যে। এখন এই প্লাগিনটা ইউজ না করলেও ম্যানুয়ালিও আপনি index.html বানিয়ে public ডিরেক্টরিতে রেখে কাজ করতে পারবেন। তবে এই প্লাগিন আমাদের কিছু কাজ জিনিস সহজ করে দিবে তাই আমরা ইউজ করবো। প্রথমে ডেভ ডিপেন্ডেসি হিসাবে নামিয়ে ফেলুনঃ

npm install --save-dev html-webpack-plugin

এখন এইটার কনফিগারেশন গুলো মেনশন করে দিতে হবে webpack.config.js ফাইলের ভিতরে। যেহেতু এটা ওয়েবপ্যাকের প্লাগিন তাই এটা entry, output এর পরে plugins নামে একটা অ্যারের ভিতরে যাবে। এবং অবশ্যই আগে এটা require করিয়ে নিতে হবে।

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  plugins: [
    new HtmlWebpackPlugin()
  ]
}

এখন এভাবে রেখে যদি npm run build দেই তাহলে html-webpack-plugin একটা ব্যাসিক index.html ফাইল ক্রিয়েট করে দিবে dist ডিরেক্টরিতে। এখানে দেখুন অটোম্যাটিকভাবেই আপনার বান্ডেল ফাইলটা লিঙ্ক করে দিয়েছে এই প্লাগিনটা, আমরা ব্যাসিকালি এই কাজের জন্যেই এটা ইউজ করতেছি। কিন্তু রিঅ্যাক্ট এ আমাদের একটা ইলিমেন্ট সিলেক্ট করতে হবে রিঅ্যাক্ট ঐখানে রেন্ডার করার জন্যে। তাই আমাদের এই html-webpack-plugin কিছু অপশন পাস করে দিতে হবে যাতে আমাদের তৈরী করা স্ট্রাকচারের উপরই index.html ফাইলটা জেনারেট করে। কনভেনশনালি public ডিরেক্টরিতেই আমাদের স্ট্রাকচারের index.html ফাইল রেখে প্লাগিনটাকে বলে দিতে হবে এটা দেখে দেখে প্রোডাকশন ভার্শনের ফাইল জেনারেট করো। তাই প্রথমে ফাইলটা বানিয়ে নিনঃ

mkdir public
touch public/index.html

এখন index.html এর ভিতরে আপনি আপনার স্ট্রাকচার রাখবেন। ব্যাসিকালি আপনার একটা ইলিমেন্ট লাগবে যেটাতে রিঅ্যাক্ট রেন্ডার হবে। অনেকটা এরকমঃ

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>React Starter Pack From Scratch</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

এখন আমাদের এখানে বান্ডেল ফাইলের কথা মেনশন করতে হবে না, এটা অটোম্যাটিকালি প্লাগিনটাই করে দিবে। তারজন্যে webpack.config.js এর html-webpack-plugin এর জন্যে কিছু অপশন পাস করে দিতে হবে এরকমঃ

...
    new HtmlWebpackPlugin({
      template: './public/index.html'
    })
...

এখানে কোন ফাইল থেকে index.html নিতে চাচ্ছেন সেটার লোকেশান মেনশন করে দিতে হবে। আর এগুলো সব html-webpack-plugin এর ডকুমেন্টেশন থেকে জানতে পারবেন। আরো ইউজফুল অনেক কিছু আছে তাই ডকুমেন্টেশন চ্যাক করতে পারেন চাইলে।

এখন আমরা রিঅ্যাক্ট এ সাধারণত jsx লিখি, জাভাস্ক্রিপ্ট এর ES6 ভার্শনের কোড লিখি, কিন্তু এগুলো আমাদের সব ব্রাউজার ফ্রেন্ডলি না। তাই আমাদের এই কোডগুলোও কম্পাইল করে বান্ডেলে নিতে হবে যাতে আমাদের সব ব্রাউজার বুঝতে পারে। এজন্যে আমাদের babel-coreব্যাবেল কোর কম্পাইলারের জন্যে, babel-loaderজাভাস্ক্রিপ্ট কোড ট্রান্সপাইলিং এর জন্যে, babel-preset-env আপনার ইনভারোমেন্ট ডিটেক্ট করে হায়ার ভার্শনের জাভাস্ক্রিপ্ট(ES6+)কে সাপোর্টের ভার্শনের জাভাস্ক্রিপ্ট(ES5) এ নেওয়ার জন্যে এবং babel-preset-react jsx কে কনভার্ট করার জন্যে ইউজ করতে হবে। এই প্যাকেজ সবগুলো ডেভ ডিপেন্ডেসি হিসেবে নামিয়ে ফেলুনঃ

npm install --save-dev babel-core babel-loader
npm install --save-dev babel-preset-env babel-preset-react

এখন এই ব্যাবেলের জন্যেও কনফিগারেশন লাগবে। কনফিগারেশন দুইভাবে করা যায়। রুট ডিরেক্টরিতে .babelrc নামে একটা ফাইল বানিয়ে ঐখানেও কনফিগারেশনগুলো রাখতে পারবেন আবার চাইলে package.json এই babel নামে কনফিগারেশনগুলো রাখতে পারবেন। আমরা এখানে package.json এই রাখবো। নিচের এগুলো লিখে ফেলেন package.json এঃ

...
"babel": {
    "presets": [
      "env",
      "react"
    ]
 }
...

এখানে আমরা ইন্সটল করা ব্যাবেলের দুইটা প্রিসেট babel-preset-env আর babel-preset-react ইউজ করতে চাচ্ছি তাই এভাবে কনফিগার করে নিতে হবে।

এখন আমাদের webpack.config.js এ ব্যাবেলকে ইউজ করার জন্যে কনফিগার করে দিতে হবেঃ

module: {
  rules: [
    {
      test: /\.js$/, //সব ফাইল js বা jsx গুলোর উপরে অ্যাপ্লাই করার জন্যে
      exclude: /node_modules/, // node_modules বাদ দিয়ে
      use: {
        loader: 'babel-loader'
      }
    }
  ]
}

webpack.config.js ফাইলটা এখন এরকম দেখা যাবেঃ

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/, 
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html'
    })
  ]
}

এবার আমাদের সবকিছু অনেকটাই তৈরী। এখন src এর ভিতরের index.js থেকে রিঅ্যাক্ট কোড লিখেই npm run build দিলে দেখবেন প্রোডাকশন ভার্শন রেডী।

এবার কিছু রিঅ্যাক্ট দিয়ে খেলাধুলা করা যাক। কিন্তু তার আগে অবশ্যই react এবং react-dom ডিপেন্ডেসি হিসাবে ইন্সটল করতে ভুলবেন না।

npm install --save react react-dom

এবার index.js ওপেন করে ভিতরে রিঅ্যাক্ট লিখা শুরু করুন

import React from 'react';
import ReactDOM from 'react-dom';

const Root = () => {
  return (
    <h1>Hello World</h1>
  )
}

ReactDOM.render(<Root />, document.getElementById('app'));

এখন এখানে index.js আপনার এন্ট্রি পয়েন্ট, তারপর ভিতরে যতই Component, Package বা আর যা দিয়েই কাজ করেন না কেনো সবই এটার সাথে কোনো না কোনোভাবে অ্যাড থাকবে আর এভাবেই ওয়েবপ্যাক আপনার ফাইলগুলো বান্ডেল করে ফেলবে।

ডেভেলপমেন্ট সার্ভার তৈরী

এবার এ তো গেলো কিভাবে ওয়েবপ্যাক কনফিগার করতে হবে। কিন্তু আমাদের ডেভেলপমেন্ট এর সময় লাইভ ডেভেলপমেন্ট সার্ভারেরও প্রয়োজন হয়। তাই এবার আমরা সেটা কিভাবে বানাবো সেটাই দেখবো। একবার ওয়েবপ্যাক ঠিকমতো কনফিগার হয়ে গেলে webpack-dev-server ইউজ করে খুব সহজেই ডেভেলপমেন্ট সার্ভার বানিয়ে ফেল যায়।

আপনার package.json ফাইলটা ওপেন করুন আর নতুন একটা স্ক্রিপ্ট অ্যাড করুন ডেভেলপমেন্ট এর জন্যেঃ

"scripts": {
  "start": "webpack-dev-server --mode development",
  ...
},

এখন npm start দিলেই দেখবেন আপনার সার্ভার রানিং আছে, বাই ডিফল্ট লোকালহোস্টের পোর্ট 8080 হবে। কিন্তু আমরা সবকিছু নিজের হাতে করতেছি তাই আমরা পুরো কন্ট্রোল নিয়ে নিবো। যেখানে মন চায় সেখানেই রান করাবো, যেভাবে মন চায় সেভাবেই রান করাবো। তাই কিছু ফ্ল্যাগ ইউজ করবো। পোর্ট মেনশন করার জন্যে --port , ফাইলে এডিট করার সাথে সাথেই ব্রাউজারে অটো রিলোডের জন্যে --hot এবং সবশেষে চাচ্ছি npm start দেওয়ার সাথে সাথেই লাইভ আমাদের বাই ডিফল্ট ব্রাউজারে যাতে ওপেন হয়ে যায়, সেজন্য --open ফ্ল্যাগ ইউজ করবো। সবশেষে package.json এর ভিতরে স্ক্রিপ্টগুলো এরকম দেখা যাবেঃ

"scripts": {
  "start": "webpack-dev-server --mode development --hot --open --port 4004",
  "build": "webpack --mode production"
},

এখানে পোর্ট আমি 4004 দিয়েছি, আপনি যেখানেই দিবেন, যতক্ষন পর্যন্ত ঐ পোর্ট আর কোথাও ইউজ না হচ্ছে। আপনি সেখানেই দিয়ে কাজ করতে পারবেন।

ব্যাসিকালি আমাদের কাজ এখানেই শেষ। আপনি এখন একদম আপনার নিজের হাতে বানানো প্যাক পেয়ে ফেললেন। এখন যতো ইচ্ছা নিজের মন মতো কাস্টমাইজ করে, নিজের প্রয়োজনমতো যা ইচ্ছা করতে পারবেন। কিন্ত আমি আরেকটা জিনিস দেখবো এখানে যেটা আসলে ইউজফুল। আপনি এখন CSS ইউজ করতে পারবেন না। এজন্যেও আরো কিছু মডিউল ইউজ করতে হবে আপনাকে।


CSS সাপোর্টেড বান্ডেলঃ

CSS সাপোর্ট করানোর জন্যে আপনাকে আরো দুইটা প্যাকেজ ডেভ ডিপেন্ডেসি হিসাবে নামিয়ে ইউজ করতে হবে ওয়েবপ্যাকের মডিউল হিসাবে।

npm install --save-dev style-loader css-loader

এখন এই মডিউলগুলো webpack.config.js এর ভিতরে কনফিগার করে দিতে হবেঃ

{
  test: /\.css$/,
  use: [ 'style-loader', 'css-loader' ]
}

এটা অ্যাড করার পর webpack.config.js অনেকটা এরকম দেখাবেঃ

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }, {
        test: /\.css$/,
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html'
    })
  ]
}

এখন আপনি import ‘./index.css’ এভাবে স্টাইল ইম্পোর্ট করতে পারবেন রিঅ্যাক্টের ভিতরে। এগুলো বান্ডেল হয়ে যাবে সবকিছুর সাথে একসাথে।

ইমেজ সাপোর্ট অ্যাড করাঃ

সেইমভাবে এজন্যেই file-loader নামে একটা প্যাকেজ ইউজ করতে হবে এবং এটার জন্যেও ওয়েবপ্যাকে কনফিগার করে নিতে হবে এরকমঃ

module: {
  rules: [
    ..., 
    ..., {
      test: /\.(gif|png|jpe?g|svg)$/i,
      use: [
          {
            loader: 'file-loader',
            options: {
              name: '[path][name].[ext]'
            }
          }
      ]
    }
  ]
},

এখন চাইলেই রিঅ্যাক্ট এ আপনি ইমেজ এভাবে, import favicon from ‘./favicon.png’ ইম্পোর্ট করে ইউজ করতে পারবেন <img src={favicon} />dist এর ভিতরে একটা src নামে ডিরেক্টরি তৈরী হয়ে ইমেজগুলো স্টোর হবে। আপনি চাইল ডকুমেন্টেশন থেকে আরো মডিফাই, নিজের মনমতো করে করতে পারবেন।

আজকে এই পর্যন্তই। আশা করি কিভাবে কি হচ্ছে সবকিছু সম্পর্কে অন্তত আমার লেখা দেখে ধারণা করতে পারবেন। আর অবশ্যই যে প্যাকেজগুলোর কথা এখানে বলা হয়েছে সেগুলোর ডকুমেন্টেশন দেখে দেখে আরো অনেকভাবে মডিফাই করে নিজের মতো করে বানিয়ে নিতে পারবেন।

আমার এই কোর্সে বানানো প্যাক/ফাইলগুলো গিটহাবে পাবেন। এটার ব্রাঞ্চ এ আপনি স্পেসেফিক ভার্শনগুলোও পাবেন। চাইলে গিটহাব থেকে দেখে নিতে পারেন। এই ভার্শনটা একটু মডিফাই করা হয়েছে যাতে কেউ চাইলে এটা স্টার্টার প্যাক হিসাবে ইউজ করতে পারে।

শেয়ার করুন

লেখাটি ভাল লাগলে সোশ্যাল মিডিয়ায় শেয়ার করুন। আপনার কলিগ, বন্ধু কিংবা প্রিয় কারও কাজে লাগতে পারে। জানেন তো, শেয়ারিং ইজ কেয়ারিং!

সাবস্ক্রিপশন সেন্টার

প্রতিদিন ওয়েবসাইটে আসা আপনার জন্য কষ্টকর হতে পারে। তাই যখনই আমি নতুন ব্লগ পোস্ট, সিরিজ, বই বা ভিডিও পাবলিশ করব,
তখনই তা আপনার ইমেইলে পেতে সাবস্ক্রাইব করুন। নো স্প্যামিং প্রমিজ!