Requiring and Resolving Paths
A lot of what Webpack does is require things. As we'll see in this section, Webpack makes a clear yet subtle distinction between those different types of things that it requires: files and modules.
More importantly, Webpack uses different logic and configuration options to resolve files and modules. Thus, it's important to understand how Webpack handles files and modules so you can debug and write your configuration accordingly.
Files and Modules
In the world of Webpack, you're either working with files or modules. Files are the source code files found in your project that you write (though, they could be 3rd party files that you put in a vendor folder).
Modules are external packages, typically added to your project with a package manager like npm
or bower
.
Stop for a moment and digest the different between files and modules -- Webpack treats them differently, so it's important you understand this distinction.
Paths: Absolute, Relative, and Module
When Webpack parses the string passed require
function, it tries to determine whether you're requiring a file or a module based on the structure of the require string.
- Absolute:
require("/var/www/project/shared/assets/app.js")
- Relative:
require("./js/app.js")
- Module:
require('bootstrap')
or `require("bootstrap/"
In practice, you're not going to encounter the absolute form very often, if ever. You'll mostly be requiring files with a relative path or a module path.
The reason this is significant is that files and modules are governed by different configuration options.
Consider the example below, in which we have three files, the root App.jsx
, which requires components/ListView.jsx
, which itself requires the underscore
3rd party module and ItemView.jsx
, another file in our project.
Breaking It Down: Requiring a File
Let's start at the top, where App.jsx
requires ListView
with this require:
require("./components/ListView");
There are two things to notice here. First, we're using a relative path by starting the require with ./
, which means "relative to the current directory of the location of the requiring file". Second, we haven't specified the file's extension, but by properly configuring resolve.extensions
Webpack will be able to find the correct file.
To satisfy this require statement, webpack will use two configuration variables: context
and resolve.extensions
.
To quote the Webpack documentation:
The context directory is the directory of the resource file that contains the require statement. If there is no resource file the configuration option
context
is used as context directory. (This can occur for entry points or with loader-generated files).The relative path is joined to the context directory and the resulting absolute file is resolved according to “Resolving an absolute path”.
Relevant Configuration Options
- context - Used when there is no resource file.
- resolve.extensions - Used to find a file when no extension is specified in the require statement.
Breaking It Down: Requiring a Module
Next let's look at ListView.jsx
. This component has two require statements, one that requires underscore
, and another that requires ItemView.jsx
.
var _ = require("underscore");
Because this has no file path at the beginning of the string, webpack will assumes that this is a module. To satisfy module requires, Webpack uses the resolve.modulesDirectories
configuration option, and looks for a file or directory named "underscore" in one of those directories. If it finds a directory with that name, it will attempt to resolve it using the NodeJS standard: look for a package.json
file at the root of the directory named "underscore" -- if package.json
has a main
field that points at a file, require that file. If the main
is not present, webpack looks for a file called index.js
at that root of that directory.
However, you're not limited to resolve.modulesDirectories
. You can use resolve.root
to specify search paths that will be prepended to resolve.modulesDirectories
(e.g. given a higher lookup priority), and resolve.fallback
to specify search paths that are appended to resolve.modulesDirectories
(given a lower priority).
Here's what the webpack documentation says about resolving module paths:
For resolving a module we first gather all search directories for modules from the context directory. This process is similar to the node.js resolving process, but the search directories are configurable with the configuration option
resolve.modulesDirectories
. In addition to this the directories in the configuration optionresolve.root
are prepended and directories in the configuration optionresolve.fallback
are appended.The module is looked up in each module directory and resolved according to “Resolving an absolute path”. If the first match has no success, the second is tried and so on.