URL Pathname Resolver
The `resolve-pathname` package, currently at stable version 3.0.0, offers a pure JavaScript implementation for resolving URL pathnames. Its core purpose is to replicate the exact pathname resolution behavior found in web browsers when processing the `href` attribute of an `<a>` tag. This utility is distinct from Node.js's `url.resolve`, which handles full URLs, and from other browser-specific solutions like `resolve-url` that may have DOM dependencies. It prides itself on 100% compatibility with browser pathname resolution rules without relying on a DOM environment. The package is typically very stable with an infrequent release cadence for major versions, indicating a mature and well-tested codebase. It is suitable for both Node.js and browser environments, with ESM, CommonJS, and UMD builds available.
Common errors
-
TypeError: resolvePathname is not a function
cause Attempting to import `resolvePathname` as a named export when it is a default export, or incorrect module syntax (e.g., `import` in a CommonJS context).fixFor ES Modules, use `import resolvePathname from 'resolve-pathname';`. For CommonJS, use `const resolvePathname = require('resolve-pathname');`. -
Path resolved to unexpected absolute path (e.g., `/my-path` instead of `/base/my-path`)
cause The `from` (base) pathname argument was omitted or was `null`/`undefined`, causing `resolve-pathname` to default the base to `/`.fixAlways provide the `from` argument, e.g., `resolvePathname('my-path', '/base');` or `resolvePathname('my-path', window.location.pathname);`.
Warnings
- gotcha This package is designed exclusively for resolving *pathnames*, not full URLs (e.g., it ignores scheme, host, port, query, hash). Do not confuse it with Node.js's built-in `url.resolve` which operates on full URLs.
- gotcha When the `from` (base) pathname argument is omitted or falsy, `resolve-pathname` defaults it to `/`. This can lead to unexpected absolute paths if you intended a different base.
- gotcha The behavior of `resolve-pathname` with trailing slashes on the `from` path is identical to browsers. For instance, `resolvePathname('bar', '/foo/')` results in `/foo/bar`, whereas `resolvePathname('bar', '/foo')` results in `/bar` (effectively navigating up from '/foo' as a file).
- gotcha Using `require()` for this package in an ESM-only environment (or vice-versa) can lead to module resolution errors, especially in modern Node.js or bundlers that enforce ES modules.
Install
-
npm install resolve-pathname -
yarn add resolve-pathname -
pnpm add resolve-pathname
Imports
- resolvePathname
import { resolvePathname } from 'resolve-pathname';import resolvePathname from 'resolve-pathname';
- resolvePathname
import resolvePathname from 'resolve-pathname';
const resolvePathname = require('resolve-pathname'); - resolvePathname
resolvePathname();
window.resolvePathname;
Quickstart
import resolvePathname from 'resolve-pathname';
// Example 1: Resolving a relative path from a specific base path.
const resolved1 = resolvePathname('about', '/company/jobs');
console.log(`'/company/jobs' + 'about' -> ${resolved1}`); // Expected: /company/about
// Example 2: Resolving a parent path from a nested base path.
const resolved2 = resolvePathname('../jobs', '/company/team/ceo');
console.log(`'/company/team/ceo' + '../jobs' -> ${resolved2}`); // Expected: /company/jobs
// Example 3: Resolving a path when no base path is provided (defaults to '/').
const resolved3 = resolvePathname('about');
console.log(`'/' + 'about' -> ${resolved3}`); // Expected: /about
// Example 4: Resolving an absolute path (base path is ignored).
const resolved4 = resolvePathname('/about');
console.log(`'/company/info/some-path' + '/about' -> ${resolved4}`); // Expected: /about
// Example 5: Index paths (trailing slash on base) are supported, similar to browsers.
const resolved5 = resolvePathname('new-page', '/company/info/');
console.log(`'/company/info/' + 'new-page' -> ${resolved5}`); // Expected: /company/info/new-page
// Simulating browser environment with window.location.pathname
// Assume window.location.pathname is '/company/team/ceo'
const mockLocationPathname = '/company/team/ceo';
const resolvedBrowser1 = resolvePathname('cto', mockLocationPathname);
console.log(`'${mockLocationPathname}' + 'cto' -> ${resolvedBrowser1}`); // Expected: /company/team/cto
const resolvedBrowser2 = resolvePathname('../jobs', mockLocationPathname);
console.log(`'${mockLocationPathname}' + '../jobs' -> ${resolvedBrowser2}`); // Expected: /company/jobs