Using webpack 2 and NPM to bundle various resources of a web application part 3: configuring and executing webpack
August 25, 2017 Leave a comment
Introduction
In the previous post we started setting up a minimal project for our webpack demo. We installed Node.js so that we can create an NPM project where NPM stands for Node Package Manager. We also installed webpack as a development dependency and discussed the difference between development and production dependencies in an NPM project. We went through the role of the package.json file and saw where the dependencies are installed on disk. Finally we added two JS files to our src folder but left them empty.
In this post we’ll first briefly discuss dependencies between JS modules. Then we’ll go ahead and configure webpack, activate it for our demo project and run it using NPM.
JavaScript module dependencies
So now we have two empty JS files in the src folder: index.js and utils.js. The target is to create a dependency between these two modules. index.js will call upon a function in utils.js to produce a greeting for the user. Add the following function to utils.js:
const greeting = (firstName, lastName ) => `Welcome to webpack ${firstName} ${lastName}`
If you don’t recognise the above code as JavaScript then you might want to go through the new features of ES6 available on this page. This is a fat arrow function which accepts the first and last name as inputs and returns a simple string as a greeting.
We now want to make this function available in index.js. We need to look at two sides of the dependency graph. First, the function(s) that are required in other parts of the application should be made public so that they can be accessed elsewhere. Second, the JS file that wants to use the public function of another JS file must be able to import it.
The challenge is that by default all functions and variables are scoped to the containing file in JS, i.e. they cannot be referenced from within another JS source file. JS lacks the private/public access modifiers available in OOP languages like Java, C#, C++ etc., so we cannot just write “public const greeting…” to make a function public. Likewise, the referencing file cannot easily import a dependency by a statement like “using” in C# or “import” in Java. To be exact, there was no straightforward way to achieve this before ES6 / ES2015 came along. This problem is not new and there are several libraries and frameworks to get around the JS module loading problem. Since we’re working with Node.js it’s worth mentioning the dependency loader employed there: CommonJS with its “require” and “module.exports” statements. “Require” is analogous to using/import and “module.exports” is similar to public elements in OOP languages. You’ll often see this syntax in Node.js web applications. If you’re interested in JS module loading and the various options and libraries around it you can consult this post.
In this tutorial we’ll go with the new ES6 way of importing and exporting dependencies using the “import” and “export” statements. We’ll have one exception, namely in the webpack configuration file as we’ll soon see. Update index.js with the following content:
import greeting from './utils' let myGreeting = greeting('John', 'Smith') console.log(myGreeting)
…and here’s utils.js:
const greeting = (firstName, lastName ) => `Welcome to webpack ${firstName} ${lastName}` export default greeting
In utils.js we declare our simple greeting function and export it for public usage using ES6 syntax. Index.js first imports the greeting function from the utils.js file, invoke it with a first and last name and show the result in the console log.
Webpack configuration
Webpack has a configuration file which must be called webpack.config.js. Webpack will be looking for that file so make sure you name it like so. Add this configuration file to the root directory of our demo project:
This configuration file must have a property called “config” which then must be exported at the end of the file. The config property in turn must include two elements: the entry point of the application and where to save the bundled JS file, i.e. what the bundled JS file should be named and what its location within the project should be.
The entry point of the application must be a JS file which no other JS file depends on. This is required so that webpack can build a dependency graph out of all the export/import statements. In our case it will be index.js and you’ll very often see this same entry point all over. Index.js is similar to the Main function in OOP applications which indicates the entry point for the program, that’s where the program execution starts.
The output file name is by convention “bundle.js” although it’s not a must. However, it’s good to adhere to commonly accepted conventions and we’ll follow that here as well. The path to the file is a bit more complex since it requires an absolute file reference, not a relative one like in the case of the entry point file name. Fortunately Node.js has a module called “path” which we can use to find the path to our project. The path module must be imported into the webpack configuration file. After all this tension build-up here’s the starting content of the webpack configuration file:
const path = require('path') const config = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'build'), filename: 'bundle.js' } } module.exports = config
The path module has a resolve function which can build an OS-specific directory name. It works on Mac, Windows, Linux etc. __dirname is a Node.js keyword that refers to the current directory. Note that it’s two underscores followed by “dirname”. “build” is a folder name where the production-ready bundle should be saved. We say that the bundle should be placed within the [root project folder]/build/ folder.
Webpack activation for our NPM project
The next step is to declare webpack as the build runner in package.json. This file contains a JSON element called “scripts” with the following default content:
“test”: “echo \”Error: no test specified\” && exit 1″
Remove that content and replace it with the following:
"scripts": { "build": "webpack" }
This modification will enable the following command in the command line:
npm run build
…which in turn will execute webpack.
Running webpack
Execute the command mentioned above in the command window:
npm run build
If everything goes fine then you’ll an output similar to the following:
Hash: 4985f81de45aa2685802
Version: webpack 3.5.5
Time: 63ms
Asset Size Chunks Chunk Names
bundle.js 3.07 kB 0 [emitted] main
[0] ./src/index.js 101 bytes {0} [built]
[1] ./src/utils.js 116 bytes {0} [built]
The version shows the webpack version that was used for the build process. Then we see the time it took to build the bundle. The table of assets shows the following:
- bundle.js has a size of 3.07kB
- The first input file, i.e. the entry point index.js has a size of 101 bytes
- The second input file, i.e. utils.js has a size of 116 bytes
Bundle.js is clearly larger than the two input files together. The bundled file was saved in the build folder within the project root just as we configured it in webpack.config.js:
Open this bundle.js file in your editor and you’ll see that it really does include a lot more code than what we originally wrote. Normally webpack is not beneficial for trivial projects like our demo project but this is the best starting point for learning the tool. The code is not too easy to follow either.
We’ll briefly look at the content of the bundled file and see how to execute it in the next post.
View all posts related to JavaScript here.