arrow-rightgithublinkedinscroll-to-topzig-zag

How to focus on building your product spending zero time on the configuration with Parcel

Last Updated On

Foreword

In nowadays writing in ES6, typescript is standard practice. Using sass & less is a requirement for a project and even a small sandbox. However, building an application using the latest and greatest does not come for free. Unfortunately, not all browsers are able to understand modern technologies and that's a reason why we compile and bundle our code. When the project is growing up and ready for deployment the other must have options are minification and cachebusting. To do all of that we used to use grunt, gulp in conjunction with babel, browserfy and other packages. Lately, webpack and its plugins were able to handle all of that alone. Nevertheless, in both cases, we really need to dedicate more or less time to configure the workflow. On the other hand, we want to start coding immediately, focus all effort on the app itself and showing some progress right away, without spending too much time on the configuration.

Parcel and its features

That's why Parcel makes so much sense to exist. It appeared on Github in December 2017 and added 14.0k stars in one month (for webpack it took a year to get the same amount)! It includes the following set of features:

  • Zero configuration. Out of the box parcel can process the following types of files:

    • Styles (CSS, SCSS, PostCSS)
    • ES6 and higher
    • Typescript
    • HTML
    • Images
  • Speed. Considered to be faster than its competitors due to multicore compilation and caching

  • Hot Module Replacement. Saves the time with automatic page refresh on every code change

  • Ships with built-in web-server

Installation

Using yarn (global installation):

yarn global add parcel-bundler

Or as a dev dependency:

yarn add parcel-bundler --dev

Using npm (global installation):

npm install -g parcel-bundler

Or as a dev dependency:

npm install --save-dev parcel-bundler

Case study

Parcel can take any type of file as an input and process it. We will start with index.html that references Javascript written in ES6 (you can also find this example on GitHub):

index.html:

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>01 - Build with Parcel</title>
  <meta name="description" content="01 - Build with Parcel">
  <meta name="author" content="@s_kryvets">
</head>

<body>
  <h1></h1>
  <script src="./js/index.js"></script>
</body>
</html>

index.js:

import { HeaderContentModifier } from './headerContentModifier';
new HeaderContentModifier().modify();

headerContentModifier.js:

export class HeaderContentModifier {
  modify() {
    const headerElement = document.getElementsByTagName('h1')[0];
    headerElement.innerHTML = 'Hello World';
  }
}

After that let's run:

parcel index.html

Boom, open http://localhost:1234 and you'll see "Hello world" application. Summary of what happened:

  • Parcel picked up on reference and processed javascript.
  • It compiled ES6 down to ES5 and bundled all imports/exports.
  • It moved everything into newly created dist folder.
  • It created source maps for debugging.
  • It started static web-server which is served out of dist and watches for files changes to the process. Hot Module Replacement helps to reflect changes of the fly in the browser without refreshing it.

How many lines of config did we write? Yes, zero. This is pretty impressive.

For production build (as you may guess) run:

parcel build index.html --no-source-maps

Adding Styles with LESS, SCSS or PostCSS

The project wouldn't be complete if we wouldn't add the styles:

styles.scss:

$background: #E0E0E0;
$secondary: salmon;
body {
  background: $background;
  font-size: 170px;
  text-align: center;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  font-family: "Avenir Next", "Helvetica Neue", sans-serif
}
@mixin shadow($color, $steps, $glow) {
  $all: ();
  $all: append($all, -1px 1px 0 rgba($color, .1), comma);
  $all: append($all, 1px -1px 0 rgba($glow, .1), comma);
  $all: append($all, 1px -1px 25px $glow, comma);
  @for $i from 1 through $steps {
    $all: append($all, append($i*-1px $i*1.9px $i*1px, rgba($color, 2/$i)));
  }
  text-shadow: $all;
}
.header {
  cursor: pointer;
  color: $background;
  @include shadow( darken($background, 25%), 25, white);
  span {
    color: $secondary;
    @include shadow( darken(desaturate($secondary, 80%), 20%), 25, lighten($secondary, 10%))
  }
}

It might be not obvious at first, but styles should be imported via javascript, in our entry point file:

import { HeaderContentModifier } from './headerContentModifier';
import styles from '../scss/style.scss'; // Adding import for styles which will be automatically picked up by Parcel

new HeaderContentModifier().modify();

The result would be (source code on GitHub):

      

I was under the big impression how easy and powerful Parcel is. It requires around ~70 lines of code to achieve the same result with webpack. As was mentioned, it's also possible to use typescript and different css preprocessors like less, postcss or others.

Multiple entry points

Working with the middle/large-size codebases, dealing with multipage applications written in PHP, Java, NodeJS or popular CMS Engines Like WordPress, Joomla Drupal, Magento requires support for different entry points. In these cases, we don't want to process index.html (since it's being generated on the back-end), but compile styles and javascript. Unfortunately, this feature isn't a supported yet, but it's in the active development phase and supposed to be released in a near future. The syntax of that will be:

parcel script.js style.scss file1.html

Looking forward to having this, as it eliminates a lot of use cases with configuration. Please see GitHub issue to find out about the progress.

Bundler API

Even though Parcel branded itself as "zero configuration" tool, It's also possible to implement custom build process with Bundler API as a part of node.js program.

Let's create a file parcel.config.js (the name is completely up to you) with the following content (most parts were taken from the official website):

const Bundler = require('parcel-bundler');

// Entrypoint file location
const file = Path.join(__dirname, './index.html');

// Bundler options
const options = {
  outDir: './dist', // The out directory to put the build files in, defaults to dist
  outFile: 'index.html', // The name of the outputFile
  publicUrl: './', // The url to server on, defaults to dist
  watch: true, // whether to watch the files and rebuild them on change, defaults to process.env.NODE_ENV !== 'production'
  cache: true, // Enabled or disables caching, defaults to true
  cacheDir: '.cache', // The directory cache gets put in, defaults to .cache
  minify: false, // Minify files, enabled if process.env.NODE_ENV === 'production'
  target: 'browser', // browser/node/electron, defaults to browser
  https: false, // Server files over https or http, defaults to false
  logLevel: 3, // 3 = log everything, 2 = log warnings & errors, 1 = log errors
  hmrPort: 0, // The port the hmr socket runs on, defaults to a random free port (0 in node.js resolves to a random free port)
  sourceMaps: true, // Enable or disable sourcemaps, defaults to enabled (not supported in minified builds yet)
  hmrHostname: '', // A hostname for hot module reload, default to ''
  detailedReport: false // Prints a detailed report of the bundles, assets, filesizes and times, defaults to false, reports are only printed if watch is disabled
};

// Initializes a bundler using the entry-point location and options provided
const bundler = new Bundler(file, options);

To trigger the build run:

node parcel.config.js

Conclusion

In the modern world of web development, most of us use latest technology stack that isn't supported natively by many browsers. Parcel easily solves this problem by helping to focus on writing code and spending zero time on the configuration. However, each tool has its own use case. I wouldn't go and switch my existing webpack config in production to parcel since webpack have been here for a while and was a de-facto standard of writing an app with Reach of Vue or even a part of framework like Angular. It shines in a case of a new project, where all your effort needed to be focused on writing the code.

Speaking of Vue and React, Parcel has his own recipes for these types of projects. You can also find in-depth explanation of some internal implementation on the official website. Many many thanks to the creators!