Module Specifiers and Resolution
Module specifiers identify the modules a JavaScript file depends on, while module resolution determines which file or package that specifier refers to. The exact rules depend on the environment, such as a browser, Node.js, or a bundler.
A module specifier is the string supplied to `import` or `export`, such as `./utils.js`, `../config.js`, or `react`. Relative specifiers describe a location relative to the importing module, while package or bare specifiers name a dependency without giving a filesystem-relative path.
Resolution is the process of turning a specifier into the actual module that should be loaded. The resolver uses rules defined by the environment and its configuration, which can include file paths, package metadata, aliases, extensions, and package search locations.
For example, `import { add } from './math.js'` tells the environment to resolve `./math.js` relative to the importing module. By contrast, `import React from 'react'` uses a bare package specifier, so resolution must locate the `react` package according to the environment's package-resolution rules.
Frontend build tools may add resolution features such as path aliases, virtual modules, or automatic handling of source-file extensions. Therefore, code that resolves successfully in one toolchain is not necessarily guaranteed to resolve the same way in another environment.
A useful debugging approach is to identify the specifier category first, then determine the resolver being used, and finally inspect the candidate path or package metadata that the resolver considers.
Key points
- Relative specifiers such as `./foo.js` are resolved from the importing module's location.
- Bare specifiers such as `react` generally refer to packages or environment-provided modules.
- Absolute or URL-based specifiers can follow environment-specific rules.
- Resolution behavior varies between browsers, Node.js, and bundlers.
- Aliases and package metadata can change which physical module a specifier resolves to.
Interview questions
What is a module specifier?
A module specifier is the string used in an import or export statement to identify another module. Examples include `./utils.js`, `../config.js`, and `react`. The specifier itself does not always name a literal file path. The environment resolves it according to its own module-resolution rules.
What happens when JavaScript resolves `./utils.js`?
The resolver interprets the path relative to the module containing the import. It then applies the environment's resolution rules to determine the concrete module represented by that path. If no valid target can be resolved, the import fails. The exact details depend on the environment.
What is a bare module specifier?
A bare specifier is a module name that does not begin with a relative or absolute path marker, such as `react` or `lodash`. In package-oriented environments, it is typically resolved through dependency and package-resolution rules rather than relative to the importing file. The exact lookup behavior depends on the runtime or bundler.
Why can an import work in a bundler but fail directly in a browser?
A bundler can perform build-time resolution and transform source code before the browser receives it. It may support aliases, package metadata, extension conventions, and other features that are not equivalent to native browser module loading. The browser instead applies its own module-specifier and URL rules to the code it receives. Consequently, source code that relies on bundler-specific resolution may need transformation before being used directly by a browser.
How would you systematically debug a module resolution error?
First, classify the specifier as relative, absolute, URL-based, or bare. Next, identify the resolver that is actually handling it, such as the browser, Node.js, or a particular bundler, and inspect its configuration. Then verify the candidate path, package metadata, aliases, and relevant file names or extensions. Finally, check whether the resolved module exists and whether the build environment differs from the environment where the failure occurs.