Originally published on freeCodeCamp with a few fresh edits here.
Nothing fancy. We’re building a web application that fetches some random users from an API and displays it on the front-end. It will have no routing extraordinare. The end goal of the article is to equip you to get used to frontend tooling.
I’m using AngularJS with no boilerplate, so we that aren’t abstracted away from CLIs that leaves us breathless and in the awe of black magic funnery. Note: I’m using AngularJS and not Angular. AngularJS because I couldn’t find any posts related to AngularJS tooling and bundling.
Let’s start by creating an index file on our root directory.
But as your app will grow, it will be necessary to keep track of all your dependencies (in this case, Angular).
So a lot of people resort to having a package manager which keeps track of the versions of dependencies they use on their project. A package manager’s single most USP is to goto the GitHub of the dependency, download it in your folder, and keep track of the version downloaded. This helps you not break your code if you move your repo and later download another version.
Go ahead, install it. We’re gonna need it. When we add a dependency in our application, yarn will download the stuff and keep it inside the
node_modules folder. From then on, if you need the file, you can src-reference into your index.
yarn add angular
While we’re doing this, let’s also add
userFactory.js files in our root directory and link them up into our index file.
Problems with this approach
The order of our script tag must be in that specific order.
app.js makes the variable
app and then attaches it to the global window object. This
app variable is then used by the rest of the script files. This is often called global namespace pollution, and if you are still using this approach, don’t. Further, if we open any one JS file in any given moment, we will have no idea what the app variable holds.
Now wouldn’t it be cool if we could have a JS police that pointed out this stuff to us while we wrote?
ESLint is a linter. Kind of like a stricter version of you who pair-programs with you. Linters save time for you by debugging your code before you even run your application. Also it forces you and your team to follow clean code practices.
yarn add eslint eslint-config-airbnb eslint-config-airbnb-base -D
We’ll be using Airbnb’s style configuration that runs through our code and tells us wherever we’re writing the code in a non-standard way. The above command will install the configurations in the
node_modules folder, but we will need to tell ESLint to use these. Make a file called
.eslintrc.json and fill it up with:
The extends stack tells ESLint to use the Airbnb rules on top of its own rules. The env variable tells ESLint to not scream at us if we’re using variables like
window without initializing it. To lint through all our files, you can use a wildcard *.
Let’s run ESLint on our files and see what happens.
These are all rules defined in the Airbnb style guide. I’ll leave it up to you to fix your files. It’s always better to have a linter from the beginning. Of course, you can also switch off a particular rule. For example, if you prefer no-semicolon, or the double-quote style to single quote, you can switch them off. ESLint will give you that flexibility.
Now let’s talk about modularity.
When making large scale applications, we need to have our code well-structured so that it’s easier to scale. We put in place a separation of concerns by grouping code pieces in separate modules.
Before ES6, there was CommonJS.
It started off as a project in Mozilla by Kevin Dangoor and was initially named ServerJS.
This standard was adopted as a pattern where you write your piece of code and tell the environment to export that piece and then, you’d use a library like RequireJS to import the module.
If you’ve tinkered around with Node, you may find that piece of code very familiar. But there are downsides to this standard, because it’s synchronous. Which means that unless
validateUrl is required,
handleSubmit on Line 3 of
postController above isn’t executed.
The code halts.
This ideology works fine in Node.js. In Node, we can have a lot of dependencies before starting a server. For example, configuring log files, connecting to the DB on cloud, configuring secret keys. But on the front end, it is not always required.
Asynchronous Module Definition (AMD)
I like to call this format a dependency-order hungry formmat. It's better than when we write a bunch of code and and then think about ordering them manually. It grew out of Dojo's real world experience with using XHR+eval and wanting to avoid its weaknesses for the future.
It asynchronously loads modules and has a few more advantages over CommonJS patterns.
Here’s how the code looks like in AMD (I added a couple of functions). Do you see something familiar?
It kind of feels like the way we Inject Dependencies in AngularJS on Line 1. (AngularJS follows Sugar which is a level up from AMD)
No external library to call. Import/export natively supported.
Babel and ES6
Problem with this code
This code won’t work.
let in ES6 creates block scoped variables, and we can’t redefine a block scoped variable inside its own scope again.
Remember: we’re still sitting on the global scope. We’ll fix this.
The reason why I asked you to refactor the code is because I want you to use
babel on this and see the magic yourself. Time to fire up that terminal.
yarn add babel-cli babel-preset-env
This will add
Babel plugins and presets
The code goes through a series of transformations, and you can choose what kinds of transformations you want. You can have it convert arrow functions to anonymous, transform spread operators, transform
for…of loops and a lot more. These transformations are what we call plugins.
You can pick and choose what kinds of transformations you want. Groups of plugins are called
Now let's make a babel configuration file: .babelrc and place it in the root folder.
Now, if you run the following command on your terminal, babel will do its job. Go ahead, try it out:
Nifty stuff, right?
Babel gave a preview of how the files will look if it were to convert the files for us.
If only there was a tool that does all that automatically. We’d tell it to take our code, run the linter for any errors before the code hits production, and transpile it to browser compatible code.
Let’s automate the hell out of this.
First, move all JS files into a folder. And let’s use standard mnemonics and name the folder
yarn add webpack webpack-dev-server babel-loader eslint-loader -D
And now, create a
If you now fire up Webpack, you will see all files bundled up in a single file in a
Congratulations. Give yourself a pat on the back. You bundled your files so that they’re almost production ready.
Dissecting Webpack Configuration
Now let’s talk about the configuration. I’ll break it down and tell you exactly what each key is. For more, you can always read the manual.
I’ve commented most of the stuff. Here, I talk about the left out stuff:
Think of this as a chain of code-loading units in a pipeline.
The last one in the stack (
babel-loader in our case) is the first one which Webpack uses for loading the code chunks. We’re asking Webpack to go through our code and transpile it first into ES5 using the
A loader object will also need a
test key. It uses this key to find all files it needs to pick up (in our case, a regex that matches files ending with the extension dot JS). Once transpiled, proceed to the next loader (
eslint-loader in our case). And in the end, write the changes from memory to a file and dump it in the file which we’ve specified in the output object.
But that’s not what our config does. We’ve added an
enforce-pre on our ESLint loader because we want the linting first. Because the output will be a single file. And that file will be in a barely human readable format if we use minification and obfuscation (which is often the case in production). The Linter will go crazy looking at our end code. We don’t want that. So Webpack will lint first and then transpile.
Apart from these, there are many loaders you can use, be it to load your style files, your SVGs, or fonts. One loader that I almost always end up using at work is the html-loader.
In the case of Angular, when we have templates in directives/components, we can use an
html-loader in Webpack.
templateUrl: './users/partial/user.tpl.html' // instead of this, templateUrl: require('./users/partial/user.tpl.html')
Webpack thrives on a super huge community which comes up with awesome loaders with great documentation. For all your needs, chances are, there is at least one loader written.
Webpack Dev Server (devServer)
Webpack dev server is a module that comes separate from Webpack. It spins up its own server and watches the files we change. If you make any changes, the WDS will bundle it again and refresh the page. If there are errors, it will refresh the page to an overlay screen (configured by the overlay key) and show you the error right on the browser. And it’s super fast because it does all that in the memory and not on the hard storage.
Of course, to get it to work, you first need to have a base build file (that is, run Webpack at least once to have a build file). Once you have that, you can fire up this command. It will start the server and serve the static files, open the browser for you on port
8080 by default, and keep watching the changes.
But this is not the end of it if you think about it. There are still so many things you can do. At work, we use Flow for static type check while we code. A static type checker looks at your code and warns you if you’re, say, calling functions with the wrong type of arguments. You can integrate that as well in Webpack.
We also use Prettier to format our code automatically as we type. It just makes the code more readable.
Congratulations! You did it!
I live on twitter as @AminSpeaks and everywhere else as @binarybaba.
Cheers and Godspeed.