Parcel Bundler

A quick look at the new bundler.

Getting started

Parcel bundler is a new bundler that can work without any configuration. Here is a quick look at this awesome tool. First, start an NPM package and install the bundler as a dev dependency.

1
2
npm init -y
npm i -D parcel-bundler

Let us make an HTML page that includes a JavaScript file.

1
2
3
4
5
6
<html>
<body>
<div id="app"></div>
<script src="src/main.js"></script>
</body>
</html>

As you can see, it is including src/main.js file. We hope that file is a single bundle prepared for the browser - before ES6 modules land, the browser cannot usually load additional files. But our file does import code from other local files!

src/main.js
1
2
import {name} from './utils'
document.getElementById('app').innerText = `Hello ${name}`

The second file will export the name constant

src/utils.js
1
export const name = 'World'

Let us load the HTML page in the browser. Here is a command to server the file using Parcel

package.json
1
2
3
4
5
{
"scripts": {
"start": "parcel index.html"
}
}

When we run npm start we get a server watching our files and serving the page at port :1234

1
2
3
4
5
6
7
$ npm start

> [email protected] start /code/parcel-bundler-post
> parcel index.html

Server running at http://localhost:1234
✨ Built in 450ms.

We can open any browser and not only we see the Hello World message, we also can get source maps and hot module reload! Even though the source panel complains about the import syntax, the break points are working.

Hello World

The served page is interesting - it is NOT the input index.html file. Instead Parcel serves a generated file dist/index.html. That file refers to bundles JavaScript internally, even uses hashes to bust the browser cache.

dist/index.html
1
2
3
4
5
6
<html>
<body>
<div id="app"></div>
<script src="/dist/9d1eab3205c8799f59b49243d0d1e069.js"></script>
</body>
</html>

The JavaScript file dist/9d1eab3205c8799f59b49243d0d1e069.js is your typical single file produced from src/main.js by tracing all imports and bringing them together into single output file.

Production

When you are happy with your application you can produce the "production" bundle that is minified and can be served from any static server. Here is the command

package.json
1
2
3
4
5
{
"scripts": {
"build": "parcel build index.html --public-url ./"
}
}

Now you can either open local file dist/index.html or use any static server on folder dist.

CSS

What if our JavaScript code includes CSS files? Let's try it

src/main.js
1
2
3
import './app.css'
import {name} from './utils'
document.getElementById('app').innerText = `Hello ${name}`

My design skills are sick, so here is my CSS

src/app.css
1
2
3
body {
background-color: antiquewhite;
}

Same npm start command - and parcel now makes a separate CSS file in the dist folder and includes a link in the dist/index.html file!

dist/index.html
1
2
3
4
5
6
7
8
9
<html>
<head>
<link rel="stylesheet" href="/dist/9d1eab3205c8799f59b49243d0d1e069.css">
</head>
<body>
<div id="app"></div>
<script src="/dist/9d1eab3205c8799f59b49243d0d1e069.js"></script>
</body>
</html>

Ohh, and my page is classy.

Bundled CSS

Nice!

Wait, did I say "CSS"? I meant - I want to use Less!

src/main.js
1
import './app.less'
src/app.less
1
2
3
4
@dominant: antiquewhite;
body {
background-color: @dominant;
}

Nothing else needed to be done - Parcel does CSS bundling as if by magic....

TypeScript

Advantages of static types are obvious. So let's use TypeScript files! Point index.html at src/main.ts, rename our source files and sprinkle some type love there.

index.html
1
2
3
4
5
6
<html>
<body>
<div id="app"></div>
<script src="src/main.ts"></script>
</body>
</html>
src/main.ts
1
2
3
import './app.less'
import {name} from './utils'
document.getElementById('app').innerText = `Hello ${name}`
src/utils.ts
1
export const name: string = 'World'

Here is where the Parcel magic really arrives: the bundler detects missing preprocessor and installs the necessary dependencies automatically!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ npm start

> [email protected] start /code/parcel-bundler-post
> parcel index.html

Server running at http://localhost:1234
⏳ Building main.ts...
npm WARN [email protected] No description
npm WARN [email protected] No repository field.

+ [email protected]
added 1 package in 4.199s
npm WARN [email protected] No description
npm WARN [email protected] No repository field.

+ [email protected]
✨ Built in 6.21s.

All installed, and the application is working. Talk about great dev experience!

API

If you want to use the bundler's NPM module API to do your own bundling, the docs are not there yet. But luckily, the code is well organized and you should be able to figure out what to call by looking at how Parcel CLI uses its main module. Here is a quick example

out.js
1
2
3
4
5
6
7
const ParcelBundler = require('parcel-bundler')
const bundler = new ParcelBundler('./index.html', {watch: false})
bundler.bundle()
.then(result => {
console.log('bundled')
console.log(result)
})

If you execute this Node script with node ./out.js you will get a description of the produced bundle

1
2
3
4
5
6
7
8
9
$ node ./out.js
✨ Built in 1.01s.
bundled
Bundle {
type: 'html',
name: 'dist/index.html',
parentBundle: undefined,
...
}

The result describes the main output file dist/index.html and all linked resources (JS and CSS bundles). You can just grab the files in the dist folder and use them.

You can also bundle files in the watch mode and get notified whenever the source files are changed - Parcel will watch all source files for you.

out.js
1
2
3
4
5
6
7
8
9
const ParcelBundler = require('parcel-bundler')
const bundler = new ParcelBundler('./index.html', {watch: true})
bundler.on('bundled', () => {
console.log('inputs changed, rebuilt bundle')
})
bundler.bundle()
.then(result => {
console.log('bundled')
})

Start this script, and once it is running change one of the source files, for example src/utils.ts - notice that the bundle has been automatically rebuilt.

1
2
3
4
5
6
$ node ./out.js
✨ Built in 1.12s.
inputs changed, rebuilt bundle
✨ Built in 178ms.
inputs changed, rebuilt bundle
^C

Great job, Parcel team