JS

ParcelJs vs Webpack

Now those of you that follow my blog will know I normally don’t blog about JavaScript. That is not to say I don’t try and keep an eye on it.

Not being big headed here, I am in fact fairly ok with talking Webpack/React/Redux/Babel with seasoned WebDevs, and have done a few projects using the following bits and bobs

  • Babel
  • SCSS (compass CSS)
  • Typescript
  • React
  • Bootstrap
  • ES6 modules

My defacto setup has always to use these tools:

  • NPM for module installation
  • Webpack
    • Modules
    • Source maps
    • Minification
    • Hashing of file names
    • Running one transpiler through another (Typescript –> Babel –> JS)
  • Babel for polyfilling features

I have been fairly happy with this in the past. Ok from time to time you have to figure out why LibraryX doesn’t play nice with LibraryY, which is a right PITA, but eventually you get there (if you ignore major updates to Webpack which seemed to break a lot of stuff)

Typical Webpack.config

This has been a pretty typical webpack.config file (for DEV I have a different one for PROD, where I minify/have no source map etc etc)

let path = require('path');

let ExtractTextPlugin = require('extract-text-webpack-plugin');

let babelOptions = {
    "presets": ["es2015", "react"]
};

let entries = {
    index: './src/index.tsx'
};


module.exports = {
    entry: entries,
    context: __dirname,
    resolve: {
        extensions: [".tsx", ".ts", ".js", ".jsx"],
        modules: [path.resolve(__dirname, "src"), "node_modules"]
    },
    mode: 'development',
    devtool: "eval-source-map",
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [

        //scss/sass files extracted to common css bundle
        new ExtractTextPlugin({
            filename: '[name].bundle.[hash].css',
            allChunks: true,
            disable: true
        }),
    ],
    module: {
        rules: [
            // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader' 1st 
            // then 'babel-loader'
            // NOTE : loaders run right to left (think of them as a cmd line pipe)
            {
                test: /\.ts(x?)$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: babelOptions
                    },
                    {
                        loader: 'awesome-typescript-loader'
                    }
                ]
            },


            // All files with a .scss|.sass extenson will be handled by 'sass-loader'
            {
                test: /\.(sass|scss)$/,
                exclude: /node_modules/,
                //loader: ExtractTextPlugin.extract(['css-loader', 'sass-loader'])
                loader: ['style-loader','css-loader', 'sass-loader']
            },

            // All files with a '.js' extension will be handled by 'babel-loader'.
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: babelOptions
                    }
                ]
            },

            {
                test: /\.png$/,
                include: /node_modules/,
                loader: "url-loader?limit=100000"
            },

            {
                test: /\.jpg$/,
                include: /node_modules/,
                loader: "file-loader"
            },

            {
                test: /\.woff(\?.*)?$/,
                include: /node_modules/,
                loader: 'url-loader?prefix=fonts/&name=fonts/[name].[ext]&limit=10000&mimetype=application/font-woff'
            },

            {
                test: /\.woff2(\?.*)?$/,
                include: /node_modules/,
                loader: 'url-loader?prefix=fonts/&name=fonts/[name].[ext]&limit=10000&mimetype=application/font-woff2'
            },

            {
                test: /\.ttf(\?.*)?$/,
                include: /node_modules/,
                loader: 'url-loader?prefix=fonts/&name=fonts/[name].[ext]&limit=10000&mimetype=application/octet-stream'
            },

            {
                test: /\.eot(\?.*)?$/, loader: 'file-loader?prefix=fonts/&name=fonts/[name].[ext]'
            },

            {
                test: /\.svg(\?.*)?$/,
                include: /node_modules/,
                loader: 'url-loader?prefix=fonts/&name=fonts/[name].[ext]&limit=10000&mimetype=image/svg+xml'
            },

            // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
            {
                enforce: "pre",
                test: /\.js$/,
                loader: "source-map-loader"
            }
        ]
    }
};

 

So I was working with our in-house web expert, who said “hey you should have a look at ParcelJS, it’s much easier than WebPack”.

ParcelJS

My initial reaction was ok, I’ll take a look……This is the first thing you see when you go to its website

image

ZERO? Did I just read that right? ZERO it said, yep ZERO. Ok lets put that to the test

I have this small github repo which I used to test it : https://github.com/sachabarber/ParcelJS-demo

image

This repo contains the following

  • Simple HTML page
  • NPM packages.json
    • React
    • Bootstrap
    • ….
  • Some custom React TSX (Typescript react files)
  • Some custom SCSS files

Lets look at some of these, in reverse chronological order

Custom SCSS files

Here is one of my SCSS files see how it uses ES6 imports

@import './variables.scss';

body {
    background-color: $body-background;
    font-size:12px;
}

 

And here is an example Typescript React file, again note the imports, and the fact it is actually TypeScript here, and that this file also imports a node_modules BootStrap.css file



import * as React from "react";
import * as ReactDOM from "react-dom";
import "/node_modules/bootstrap/dist/css/bootstrap.min.css";


import {
    Nav,
    Navbar,
    NavItem,
    NavDropdown,
    MenuItem,
    Button
} from "react-bootstrap";
import { Router, Route, hashHistory } from 'react-router';
import { Details } from "./Details";
import { Orders } from "./Orders";
import "../scss/index.scss";


class MainNav extends React.Component<undefined, undefined> {

    constructor(props: any) {
        super(props);
    }

    render() {
        return (
			<Navbar collapseOnSelect>
				<Navbar.Header>
					<Navbar.Brand>
						<span>Simple React-Redux example</span>
					</Navbar.Brand>
					<Navbar.Toggle />
				</Navbar.Header>
				<Navbar.Collapse>
					
<Nav pullRight>
                        <NavItem eventKey={2} href='#/detailsPage'>Details</NavItem>
                        <NavItem eventKey={2} href='#/ordersPage'>Orders</NavItem>
					</Nav>

				</Navbar.Collapse>
			</Navbar> 
        )
    }
}

class App extends React.Component<undefined, undefined> {
    render() {
        return (
            
<div>
                
<div>
                    <MainNav/>
                    {this.props.children}
                </div>

            </div>

        )
    }
}


ReactDOM.render((
    <Router history={hashHistory}>
        <Route component={App}>
            <Route path="/" component={Details} />
            <Route path="/detailsPage" component={Details} />
            <Route path="/ordersPage" component={Orders} />
        </Route>
    </Router>
), document.getElementById('root'));
</pre>

HTML file

This is the HTML file I use for the app. Notice how there is NO CSS / JS in the head section at all. All there is, is a single reference to the main entry point JSX file Index.tsx. That is it.

 

<html>

<head>
	
</head>
<body>
  <div id="root"></div>
  <script src="./src/index.tsx"></script>
</body>
</html>

 

Running it all

So that’s it, you will not find ANY configuration what so ever. ParcelJS just deals with it. In order to test this out lets do the following

Ensure you have done the following

  • npm install
  • npm install -g parcel-bundler
  • open directory to wwwroot folder in nodeJS command prompt and issue this command parcel index.html
  • open browser and you should see the following

alt text

WOW that’s pretty nuts. What does this prove? Well quite a lot actually:

  • ES6 modules are working with ZERO config
  • Our entry point Js file is working with ZERO config
  • Typescripts –> Babel –> standard Js transpiling is working with ZERO config
  • SCSS transpiling is working with ZERO config

 

That’s not all, See how source maps are on my default

 

alt text

What Does It Actually Produce With This Magic?

There are some sensible defaults at play with Parcel, we just saw one of them auto Source-Maps unless stated otherwise. Where stuff gets generated is another sensible default of “dist

Lets have a look at the default output see what we get:

image

  • Boostrap glyphs
  • Some hash named CSS/JS files (good as we will see cache misses when these change names)
  • Source map
  • And we get this HTML file created all without the use of the HtmlWebpackPlugin

 

<html>

<head>

<link rel="stylesheet" href="/src.aeb21ac6.css"></head>
<body>
<div id="root"></div>
<script src="/src.aeb21ac6.js"></script>
</body>
</html>

 

Lets just take a breather there. I did not write one single line of config (go check out the Webpack.config file again), I seem to have the same functionality as that, without writing one line of stuff. That’s pretty A-MAZING if you ask me.

PROD Release

For prod you would likely want to use the following command parcel index.html –no-source-maps. You can read more on the ParcelJs CLI section : https://parceljs.org/cli.html

Conclusion

I have to say it does what it says on the tin. I guess if you want really fine grained control over EVERYTHING, WebPack will do you proud, but if you are happy with some  BLOODY SENSIBLE DEFAULTS being applied I have to say I was up and running with Parcel in minutes.

Its awesome

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s