How to setup a TypeScript project using Rollup.js

Originally published at labs.thisdot.co

Dec 29, 2020 · 7 minutes read · [TypeScript] [Rollup.js]

A couple of months ago I started to work through web standards, Rollup.js, and TypeScript. It’s my first experience using Rollup although I’ve been working with TypeScript on different projects and using it along with frameworks like Angular.

So far it has been a good experience in terms of performance and tools integration, so I decided to write a quick tutorial to have a setup ready using it.

Rollup with TypeScript

What is Rollup?

In words of the official documentation:

Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application…

What does that mean? Let’s start by saying that JavaScript improved drastically since ES6, and now it’s possible to define small portions of code that can be shared using two magical keywords: import and export. This specification is supported by modern browsers and tools. Rollup allows writing a future-proof code using the new module system without missing the compatibility with other module systems like CommonJS, AMD, and others.

Rollup also is considered as the “Next-generation ES module bundler”, an alternative to the very known bundler Webpack.

Project Setup

Let’s start building a small application based in TypeScript. Our first goal would be to compile and build it using Rollup.

Prerequisites

You’ll need to have installed the following tools in your local environment:

  • Node.js. Preferably the latest LTS version.
  • A package manager. You can use either NPM or Yarn. This tutorial will use NPM.

Initialize the Project

You’ll need a new folder to get started, you can create it from a command-line interface or from your favorite IDE/Code Editor:

mkdir typescript-rollup
cd typescript-rollup

Let’s start running the first command inside that folder to create the package.json file and have an initialized project. This will be useful to manage and install packages later.

npm init -y

The previous command will create the package.json with some defaults, you can edit that file later according to your needs:

{
  "name": "typescript-rollup",
  "version": "1.0.0",
  "description": "A basic TypeScript project built using Rollup.js",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/luixaviles/typescript-rollup.git"
  },
  "keywords": [],
  "author": "Luis Aviles <@luixaviles>",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/luixaviles/typescript-rollup/issues"
  },
  "homepage": "https://github.com/luixaviles/typescript-rollup#readme"
}

Use npm init only if you want to set your preferences with the interactive mode of the command.

Source Code Files

Let’s create a library to perform basic Math operations and perform strings processing. The project will contain the following source files:

|- typescript-rollup
    |- src/
        |- math/
            |- math.ts
            |- index.ts
        |- string/
            |- string.ts
            |- index.ts
        |- app.ts

Add the Math operations in math/math.ts file:

export function add(x: number, y: number): number {
  return x + y;
}

export function substract(x: number, y: number): number {
  return x - y;
}

Then, the strings operations will be defined in string/string.ts file:

export const toLowerCase = (input: string): string => {
  return input.toLocaleLowerCase();
};

export const toUpperCase = (input: string): string => {
  return input.toUpperCase();
};

In the latest file, we’re exporting a function using the ES6 Arrow function expression.

In order to import related functions regardless of the filenames, we can create the index.ts files in both directories:

// src/math/index.ts
export { add, substract } from './math';
// src/string/index.ts
export { toLowerCase, toUpperCase } from './string';

And now pay attention to the src/app.ts file:

import { add, substract } from './math';

const x = 20;
const y = 10;

console.log(`${x} + ${y} = ${add(x, y)}`);
console.log(`${x} - ${y} = ${substract(x, y)}`);

The app.ts file is the starting point of our application. Also, take a look at the first import line. We don’t need to import those functions from ./math/math.ts file, since all of them have been exported in /math/index.ts file. Again, this is a convenient way to organize the library content using TypeScript.

Installing Rollup and TypeScript

Let’s continue installing Rollup and TypeScript as development dependencies:

npm install --save-dev rollup typescript

Additional tools are needed:

  • @open-wc/building-rollup, as a rollup plugin for integration between Rollup.js and TypeScript,
  • rimraf, which is the UNIX command rm -rf for Node.js
  • deepmerge, a tool to merge enumerable properties or more objects deeply.

Use a single command to have all of them as new dependencies:

npm install --save-dev @open-wc/building-rollup rimraf deepmerge

TypeScript Configuration

For every TypeScript project, it’s required to create the tsconfig.json file. It indicates that the directory is the root of a TypeScript project.

tsc --init
message TS6071: Successfully created a tsconfig.json file.

Let’s apply some changes to that file:

{
  "compilerOptions": {
    "target": "es2018",
    "module": "esnext",
    "moduleResolution": "node",
    "noEmitOnError": true,
    "lib": ["es2017"],
    "strict": true,
    "esModuleInterop": false,
    "outDir": "out-tsc",
    "rootDir": "./"
  },
  "include": ["./src/**/*.ts"]
}

The most important thing here is the outDir option since the value says where the JavaScript files will be generated. Also, the include parameter will list a set of patterns of the files to be included in the compilation.

Learn more about the compilerOptions and the tsconfig.json file in TsConfig Reference documentation.

Rollup Configuration

It’s time to configure the module bundler tool: Rollup. You can use Rollup from a command-line interface along with parameters. However, you can provide a configuration file to simplify the usage and provide advanced functionality.

Let’s create a rollup.config.js file with the next content:

// rollup.config.js

import merge from 'deepmerge';
import { createBasicConfig } from '@open-wc/building-rollup';

const baseConfig = createBasicConfig();

export default merge(baseConfig, {
  input: './out-tsc/src/app.js',
  output: {
    dir: 'dist',
  },
});

Take a look at the documentation if you’re curious about the different ways to use these configuration files. Also, find an example of the TypeScript support provided by @open-wc/building-rollup package.

The Build script

In the package.json file, define a script to compile the input files and run the previous configurations:

{
  ...
  "scripts": {
    "build": "rimraf dist && tsc && rollup -c rollup.config.js"
  },
  ...
}

Here’s what is happening with the script.

  • rimraf dist, will make sure to clean up the output directory for Rollup: dist
  • tsc, will run the TypeScript compiler through the configurations defined in tsconfig.json file. The output content will be located in the ./out-tsc directory, as defined in the TypeScript configuration file.
  • rollup -c rollup.config.json, will run Rollup and take the ./out-tsc directory as input and put the result in a dist folder. Those configurations are defined in the rollup.config.js file.

Generate the Build and Run the App

Run the build script using:

npm run build

You’ll have the following output:

npm run build

> [email protected] build /Users/luixaviles/projects/github/typescript-rollup
> rimraf dist && tsc && rollup -c rollup.config.js

./out-tsc/src/app.js → dist...
created dist in 300ms

Finally, run the single file generated as a result of the build script:

node dist/7b857f5b.js
20 + 10 = 30
20 - 10 = 10

You’ll see the expected output.

The Final Project Structure

You should have the following project structure, including the source code files and configurations:

|- typescript-rollup
    |- src/
        |- math/
            |- math.ts
            |- index.ts
        |- string/
            |- string.ts
            |- index.ts
        |- app.ts
    |- package.json
    |- rollup.config.js
    |- tsconfig.json

Alternatives to integrate Rollup and TypeScript

In case you’re planning to build a SPA project(Single Page Application), you can use the createSpaConfig in Rollup configuration. Also, you can install the @rollup/plugin-typescript for seamless integration between Rollup and TypeScript.

// rollup.config.js
import merge from 'deepmerge';
import { createSpaConfig } from '@open-wc/building-rollup';
import typescript from '@rollup/plugin-typescript';

const baseConfig = createSpaConfig();

export default merge(baseConfig, {
  input: './index.html',
  plugins: [typescript()],
});

The @rollup/plugin-typescript will load any compilerOptions from the tsconfig.json file by default. However, there’s an option to override those configurations:

// rollup.config.js

...
export default merge(baseConfig, {
  input: './index.html',
  plugins: [typescript({target: "es5"})],
});

In case you prefer a simple configuration, then you can set the rollup.config.js file using only this plugin:

// rollup.config.js
import typescript from '@rollup/plugin-typescript';

export default {
  input: 'src/app.ts',
  output: {
    dir: 'output',
    format: 'cjs',
  },
  plugins: [typescript()],
};

Or even better, use the open-wc project generator for an automated setup through Rollup, TypeScript, and even Web Components.

Source Code of the Project

Find the complete project in this GitHub repository: typescript-rollup. Do not forget to give it a star ⭐️ and play around with the code.


You can follow me on Twitter and GitHub to see more about my work.

tweet Share