πŸ“” Module Resolution

How dependencies are resolved

The Parcel resolver implements a modified version of the node_modules resolution algorithm.

  • root directory: the directory of the entrypoint specified to Parcel, or the shared root (nearest common parent directory) when multiple entrypoints are specified.
  • project root: the nearest directory from the root direct that contains a lockfile (yarn.lock, package-lock.json, pnpm-lock.yaml) or a VCS folder (.git, .hg)

ΒΆ Absolute Paths

/foo resolves foo relative to the project root.

ΒΆ Tilde Paths

~/foo resolves foo relative to nearest node_modules directory, the nearest directory with package.json or the project root - whichever comes first.

ΒΆ package.json browser field

If a package includes a package.browser field (and it is a string), Parcel will use this instead of the package.main entry.

If it is an object, it behaves just like aliases, but has a higher priority when target.context === "browser".

ΒΆ Aliases

Aliases are supported through the alias field in package.json.

This example aliases react to preact and some local custom module that is not in node_modules.

They can also map to global variables expected to exist at runtime. This can be helpful for replacing a dependency with, for example, a version loaded from a CDN.

package.json:
{
"name": "some-package",
"devDependencies": {
"parcel-bundler": "^2.0.0-beta.1"
},
"alias": {
"react": "preact/compat",
"react-dom": "preact/compat",
"local-module": "./custom/modules",
"other-local-module": { "fileName": "./custom/other-module" },
"lodash": { "global": "_" }
}
}

Avoid using any special characters in your aliases as some may be used by Parcel and others by 3rd party tools or extensions. For example:

  • ~ is used by Parcel to resolve tilde paths.
  • @ is used by npm to for packages by npm organizations.

We advise being explicit when defining your aliases, so please specify file extensions, otherwise Parcel will need to guess. See JavaScript Named Exports for an example of this.

Prefixing global aliases with window or globalThis (e.g. window.jQuery) could fail with multi-platform builds and is not recommended. Although file aliases can be "path/to/file" instead of { "fileName": "path/to/file" }, globals must use the { "global": "name" } format.

ΒΆ Externals

Externals must be configured on a target-by-target basis with includeNodeModules. Like globals, externals will not be bundled, but they will instead be imported at runtime.

package.json:
{
"targets": {
"app": {
"includeNodeModules": {
"react": false
}
}
}
}

ΒΆ Package entry fields

When scope hoisting is enabled, a bare specified (e.g. lodash) is resolved in this order (the first field that specified and points to ane existing file):

  • package.json#source
  • package.json#browser
  • package.json#module
  • package.json#main
  • index.{js, json}

Without scope hoisting however, main is preferred to module for better performance:

  • package.json#source
  • package.json#browser
  • package.json#main
  • package.json#module
  • index.{js, json}

ΒΆ Common issues

ΒΆ Javascript Named Exports

Alias mappings apply to many asset types and do not specifically support mapping of JavaScript named exports. If you wish to map JS named exports you can re-export the named export within the aliased file:

package.json:
{
name: "some-package",
alias: {
ipcRenderer: "./electron-ipc.js", // specify file extension
},
}
electron-ipc.js:
module.exports = require("electron").ipcRenderer;

ΒΆ Flow with Absolute or Tilde Resolution

When using absolute path or tilde path module resolution you must configure Flow using the module.name_mapper feature.

Given a project with this structure and src/index.html as an entrypoint, the entry root is the src/ folder.

β”œβ”€β”€ package.json
β”œβ”€β”€ .flowconfig
└── src/
    β”œβ”€β”€ index.html
    β”œβ”€β”€ index.js
    └── components/
        β”œβ”€β”€ apple.js
        └── banana.js

Therefore, to map this import correctly, Flow should replace the leading / in '/components/apple' with src/, resulting in 'src/components/apple'. That is achieved by the following setting in your .flowconfig.

index.js:
import Apple from "/components/apple";
.flowconfig:
[options]
module.name_mapper='^\/\(.*\)$' -> '<PROJECT_ROOT>/src/\1'

<PROJECT_ROOT> is a Flow specific identifier indicating the location of your .flowconfig.

Note: module.name_mapper can have multiple entries. This enabled support for absolute or tilde Path Resolution in addition to local module aliasing support.

ΒΆ TypeScript ~ Resolution

TypeScript will need to know about your use of the ~ module resolution or alias mappings. Please refer to the TypeScript Module Resolution docs for further information.

tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~*": ["./src/*"]
}
}
}

ΒΆ Monorepo Resolution

TODO?

These are the advised usages with monorepos at this time:

Advised usage:

  • use relative paths.
  • use / for a root path if a root is required.
  • avoid ~ within monorepos.

If you're a monorepo user and would like to contribute to these recommendations, please provide example repos when opening issues to support the discussion.