An Introduction to Webpack Configs
Webpack configs allow you
to configure and extend Webpack's basic functionality. A Webpack
config is a JavaScript object that
configures one of Webpack's options. Most projects
define their Webpack config in a top-level webpack.config.js
file,
although you can also pass the config as a parameter to Webpack's Node.js API.
To understand Webpack configs, you need to understand what Webpack
does. Webpack is first and foremost a bundler. Webpack's base
functionality is to take a JavaScript file, resolve any dependencies
(require()
, import
, etc.), and output a bundled JavaScript file that
contains all those dependencies. You can then run the bundled file
without having to load those dependencies again.
Do You Even Need a Webpack Config?
For basic use cases like bundling a Vue app or a
Lambda function with Webpack,
you might not even need a Webpack config. By default, Webpack
bundles the src/index.js
file and writes the output to the
dist/main.js
file.
Suppose you have the below file in src/index.js
- it's a
"Hello, World" app using Vue.
const Vue = require('vue');
const app = new Vue({
template: '<h1>Hello, World</h1>'
});
app.$mount('#content');
If you run npm install vue webpack webpack-cli
, and run ./node_modules/.bin/webpack
, you'll see the below output:
$ ./node_modules/.bin/webpack
Hash: f19bd04db784f5de4438
Version: webpack 4.42.0
Time: 1152ms
Built at: 03/02/2020 10:18:13 AM
Asset Size Chunks Chunk Names
main.js 68.9 KiB 0 [emitted] main
Entrypoint main = main.js
[0] (webpack)/buildin/global.js 472 bytes {0} [built]
[1] ./src/index.js 116 bytes {0} [built]
+ 4 hidden modules
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
Webpack generated a bundled main.js
file that you can then load in the browser:
<html>
<head>
<script src="dist/main.js"></script>
</head>
<body>
<div id="content"></div>
</body>
</html>
So you can get the core benefits of Webpack with zero configuration. For many apps Webpack's zero config option is enough. But one place where it starts to break down is if you have multiple files that you want to bundle - say you have one GitHub repo with multiple Lambda functions. Here's how you handle multiple files with a Webpack config.
Multiple Files
This section will use 3 Webpack options. 2 are for specifying which files to bundle:
entry
: what files to bundleoutput
: where to put the bundled files
There's one more option, the target
option, which tells Webpack
whether you're bundling for the browser ('web'
) or Node ('node'
).
For Vue apps you will typically use 'web'
, but for Lambda you
should use 'node'
.
Below is a Webpack file using those 3 options that bundles 2
files from the src
directory and outputs them to the dist
directory.
module.exports = {
// You need to list out every file you want to bundle in `entry`
entry: {
express: `${process.cwd()}/src/express.js`,
mongoose: `${process.cwd()}/src/mongoose.js`
},
output: {
// Write to the '/dist' directory relative to the directory
// where `webpack` is running
path: `${process.cwd()}/dist`,
// Webpack will bundle `src/foo.js` into `dist/foo.min.js`
// because of `[name]`.
filename: '[name].min.js'
},
target: 'node'
};
Note that Webpack configs are JavaScript files, not JSON or YAML.
Here's the contents of the express.js
and mongoose.js
files:
// express.js
const pkg = require('express/package');
console.log('Express version', pkg.version);
// mongoose.js
const mongoose = require('mongoose');
console.log('Mongoose version', mongoose.version);
Webpack bundles Express and Mongoose with each function, so you
can still run express.min.js
and mongoose.min.js
even if
you rm -rf ./node_modules
.
More Sophisticated Configs
If Webpack configs are this simple, why do developers complain about Webpack being hard? Because Webpack is also a common entry point for transpilers - Babel, TypeScript, JSX, etc. If you don't need to transpile (and odds are you don't), Webpack is easy. But once you introduce transpilers, things can get tricky.
Here's the official guide for using Webpack to compile TypeScript. This section will provide an abridged version.
The key part of webpack.config.js
for transpilers is the module.rules
option. This is where you tell Webpack to use a special loader to
compile a file before bundling. For TypeScript, you need the
ts-loader npm module, in
addition to the typescript npm module.
npm install typescript ts-loader
The module.rules
option is an array of rules. The below webpack.config.js
tells Webpack to use the ts-loader
module to compile any files that end in '.ts'.
module.exports = {
entry: './src/index.ts',
module: {
// Use `ts-loader` on any file that ends in '.ts'
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
// Bundle '.ts' files as well as '.js' files.
resolve: {
extensions: ['.ts', '.js'],
},
output: {
filename: 'main.js',
path: `${process.cwd()}/dist`,
}
};
Below is the index.ts
file:
const str: string = 'Hello, World';
console.log(str);
You also need to add a tsconfig.json
file, otherwise TypeScript will error out. For the purposes of this tutorial, the below tsconfig.json
is enough:
{"files":["src/index.ts"]}
Running ./node_modules/.bin/webpack
should give you the below output:
$ ./node_modules/.bin/webpack
Hash: 63b83086be302b9d23c8
Version: webpack 4.42.0
Time: 1301ms
Built at: 03/02/2020 10:51:14 AM
Asset Size Chunks Chunk Names
main.js 957 bytes 0 [emitted] main
Entrypoint main = main.js
[0] ./src/index.ts 44 bytes {0} [built]
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
And then you can finally run node ./dist/main.js
!