TypeScript Dynamic module loading + Code splitting with PARCEL [Example App]
Tags: typescript, parcel, rxjs
Published: 2020-11-28 21:43:02

Today i will not talk much about dynamic import which is available since **TypeScript 2.4** and its officially part of ES2020 but i will demonstrate dynamic loading on demand in a small TypeScript  application in combination with parcel's code splitting feature. The goal behind is to improve performance specially in big size applications.

we have all used the **static imports** in ES6, placed on top of files,
```javascript
// Static import
import * as feature from "./module"; // static import
```
which is amazing but **what about loading modules on demand?**
```javascript
// Dynamic import
import('./feature') 
  .then((module)=> {
    module.method1();
    module.method2();
});
```
May be we want to load a module on click or navigation event or based on a condition... what if we don't want to load all our modules on startup and convert them to lazy loaded modules so our app will load *FASTER* 🚀.
i'm Boring right? 😅 lets move on to our use case, to Code 👨‍💻.

## Application ([Demo](https://findcountries.netlify.app/))
i've created a TypeScript App ** Country Search** that containq two features:
1. Search countries by spoken language (*EN*, *ES*, *AR* ...)
2. Search countries by used currency (*USD*, *EUR*, *DZD* ...)

Consuming a free api and manipulating responses as Observables because i like RXJS 😎...who cares!

So the idea behind is simple; I will not load both modules (Language Search && Currency search) on startup, instead of that modules will be loaded on section selection.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4uw3509lyfi50tcf49ex.PNG)


**INDEX.TS**
```javascript
// get currency and language buttons elements

const btnCurrency = document.getElementsByClassName("currency-btn")[0] as HTMLElement;
const btnLang = document.getElementsByClassName("lang-btn")[0] as HTMLElement;

// set current section
let selectedSection = "language";
```
In few words i switch current section based on button click event. **Then i will listen to search input changes and based on the selected section (language || currency) i load the correspondent module**
The default section is *Language* with the value 'EN'

```javascript
fromEvent(
  document.getElementById("search") as FromEventTarget<Event>,
  "input"
).pipe(
    pluck("target", "value"),
    startWith("en"),
    filter((val) => {
      return (val as string).trim().length > 1;
    }),
    distinctUntilChanged(),
    debounceTime(600),
    switchMap((val) =>
      iif(
        () => selectedSection === "language",
        from(import("./language")),
        from(import("./currency"))
      ).pipe(
        switchMap((m) => {
          const getCountries = m.default;
          return getCountries(val as string).pipe(
            catchError((error) => {
              console.log("Caught search error the right way!");
              return of([]);
            })
          );
        })
      )
    )
  )
  .subscribe(console.log);
 
```

The interesting part is the **iif** rxjs operator block where i return one of the two Observables **from(import("./language"))**  or **from(import("./currency"))** base on the selected section and then get and call the default exported function in that module (**getCountries**).

That way the currency module is never loaded until the user select that section. here is how does the currency module look like; the language one is pretty much the same.

it contains these steps in order:

1. Instantiation of CountryService (http calls)
2. Get the data
3. Build the html cards


**CURRENCY.TS** *Lazy Loaded*

```javascript
import { tap } from "rxjs/operators";
import { Country } from "~index";
import { CountryService } from "~service/country.service";

console.log("CURRENCY module is lazy loaded....");

const service = new CountryService();

export default (val: string) => {
  const searchMessage = document.getElementById("searchTitle") as HTMLElement;
  const container = document.getElementsByClassName("country-container")[0];
  container!.innerHTML = "<div></div>";
  searchMessage.textContent = "🧐 Searching ...";
  return service.getCountriesByCurrency(val).pipe(
    tap((data) => {
      if (data.length) {
        const list = data.reduce((result: string, item: Country) => {
          result += `<div class="country-item"><img class="flag" src="${item.flag}"  alt="flag"/><div class="description"><h2> ${item?.name} </h2>  <small> ${item?.region}</small> <h2>Capital: ${item?.capital}</h2></div></div>`;
          return result;
        }, "");
        container!.innerHTML = list;
        searchMessage.textContent = `${data.length} ${
          data.length === 1 ? "Country" : "Countries"
        } using ${getFullCurrencyName(data[0]?.currencies, val)}`;
      } else {
        searchMessage.textContent = `No Countries found ☹️`;
      }
    })
  );
};

function getFullCurrencyName(currencies: any[], code: string) {
  const currency = currencies.find((e) => e.code === code.toUpperCase());
  return currency ? currency.name : code;
}


```

As I said before, i've combined that with [Parceljs](https://parceljs.org/) code splitting feature. A chunk is created for each module and each one can be loaded on demand instead of loading all modules at once when the app starts.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3eto9h5p7r5hwq1blg81.gif)

#### [Demo Deployed on Netlify](https://findcountries.netlify.app/)
#### [Repo Github](https://github.com/fatehMohamed14/Webpack-vs-Parceljs)