# import-meta-resolve

[![Build][build-badge]][build]
[![Coverage][coverage-badge]][coverage]
[![Downloads][downloads-badge]][downloads]

Resolve things like Node.js.

## Contents

* [What is this?](#what-is-this)
* [When to use this?](#when-to-use-this)
* [Install](#install)
* [Use](#use)
* [API](#api)
  * [`resolve(specifier, parent)`](#resolvespecifier-parent)
  * [`moduleResolve(specifier, parent, conditions, preserveSymlinks)`](#moduleresolvespecifier-parent-conditions-preservesymlinks)
  * [`ErrnoException`](#errnoexception)
* [Algorithm](#algorithm)
* [Differences to Node](#differences-to-node)
* [Types](#types)
* [Compatibility](#compatibility)
* [Contribute](#contribute)
* [License](#license)

## What is this?

This package is a ponyfill for [`import.meta.resolve`][native-resolve].
It supports everything you need to resolve files just like modern Node does:
import maps, export maps, loading CJS and ESM projects, all of that!

## When to use this?

As of Node.js 20.0, `import.meta.resolve` is still behind an experimental flag.
This package can be used to do what it does in Node 16–20.

## Install

This package is [ESM only][esm].
In Node.js (version 16+), install with [npm][]:

```sh
npm install import-meta-resolve
```

## Use

```js
import {resolve} from 'import-meta-resolve'

// A file:
console.log(resolve('./index.js', import.meta.url))
//=> file:///Users/tilde/Projects/oss/import-meta-resolve/index.js

// A CJS package:
console.log(resolve('builtins', import.meta.url))
//=> file:///Users/tilde/Projects/oss/import-meta-resolve/node_modules/builtins/index.js

// A scoped CJS package:
console.log(resolve('@eslint/eslintrc', import.meta.url))
//=> file:///Users/tilde/Projects/oss/import-meta-resolve/node_modules/@eslint/eslintrc/lib/index.js

// A package with an export map:
console.log(resolve('micromark/lib/parse', import.meta.url))
//=> file:///Users/tilde/Projects/oss/import-meta-resolve/node_modules/micromark/lib/parse.js

// A node builtin:
console.log(resolve('fs', import.meta.url))
//=> node:fs
```

## API

This package exports the identifiers [`moduleResolve`][moduleresolve] and
[`resolve`][resolve].
There is no default export.

### `resolve(specifier, parent)`

Match `import.meta.resolve` except that `parent` is required (you can pass
`import.meta.url`).

###### Parameters

* `specifier` (`string`)
  — the module specifier to resolve relative to parent
  (`/example.js`, `./example.js`, `../example.js`, `some-package`, `fs`, etc)
* `parent` (`string`, example: `import.meta.url`)
  — the absolute parent module URL to resolve from; you must pass
  `import.meta.url` or something else

###### Returns

Full `file:`, `data:`, or `node:` URL (`string`) to the found thing

###### Throws

Throws an [`ErrnoException`][errnoexception].

### `moduleResolve(specifier, parent, conditions, preserveSymlinks)`

The [“Resolver Algorithm Specification”][algo] as detailed in the Node docs
(which is slightly lower-level than `resolve`).

###### Parameters

* `specifier` (`string`)
  — `/example.js`, `./example.js`, `../example.js`, `some-package`, `fs`, etc
* `parent` (`URL`, example: `import.meta.url`)
  — full URL (to a file) that `specifier` is resolved relative from
* `conditions` (`Set<string>`, default: `new Set(['node', 'import'])`)
  — conditions
* `preserveSymlinks` (`boolean`, default: `false`)
  — keep symlinks instead of resolving them

###### Returns

A URL object (`URL`) to the found thing.

###### Throws

Throws an [`ErrnoException`][errnoexception].

### `ErrnoException`

One of many different errors that occur when resolving (TypeScript type).

###### Type

```ts
type ErrnoExceptionFields = Error & {
  errnode?: number | undefined
  code?: string | undefined
  path?: string | undefined
  syscall?: string | undefined
  url?: string | undefined
}
```

The `code` field on errors is one of the following strings:

* `'ERR_INVALID_MODULE_SPECIFIER'`
  — when `specifier` is invalid (example: `'#'`)
* `'ERR_INVALID_PACKAGE_CONFIG'`
  — when a `package.json` is invalid (example: invalid JSON)
* `'ERR_INVALID_PACKAGE_TARGET'`
  — when a `package.json` `exports` or `imports` is invalid (example: when it
  does not start with `'./'`)
* `'ERR_MODULE_NOT_FOUND'`
  — when `specifier` cannot be found in `parent` (example: `'some-missing-package'`)
* `'ERR_NETWORK_IMPORT_DISALLOWED'`
  — thrown when trying to resolve a local file or builtin from a remote file
  (`node:fs` relative to `'https://example.com'`)
* `'ERR_PACKAGE_IMPORT_NOT_DEFINED'`
  — when a local import is not defined in an import map (example: `'#local'`
  when not defined)
* `'ERR_PACKAGE_PATH_NOT_EXPORTED'`
  — when an export is not defined in an export map (example: `'tape/index.js'`,
  which is not in its export map)
* `'ERR_UNSUPPORTED_DIR_IMPORT'`
  — when attempting to import a directory (example: `'./lib/'`)
* `'ERR_UNKNOWN_FILE_EXTENSION'`
  — when somehow reading a file that has an unexpected extensions (`'./readme.md'`)
* `'ERR_INVALID_ARG_VALUE'`
  — when `conditions` is incorrect

## Algorithm

The algorithm for `resolve` matches how Node handles `import.meta.resolve`, with
a couple of differences.

The algorithm for `moduleResolve` matches the [Resolver Algorithm
Specification][algo] as detailed in the Node docs (which is sync and slightly
lower-level than `resolve`).

## Differences to Node

* `parent` defaulting to `import.meta.url` cannot be ponyfilled: you have to
  explicitly pass it
* no support for loaders (that would mean implementing all of loaders)
* no support for CLI flags:
  `--conditions`,
  `--experimental-default-type`,
  `--experimental-json-modules`,
  `--experimental-network-imports`,
  `--experimental-policy`,
  `--experimental-wasm-modules`,
  `--input-type`,
  `--no-addons`,
  `--preserve-symlinks`, nor
  `--preserve-symlinks-main`
  work
* no support for `WATCH_REPORT_DEPENDENCIES` env variable
* no attempt is made to add a suggestion based on how things used to work in
  CJS before to not-found errors
* prototypal methods are not guarded: Node protects for example `String#slice`
  or so from being tampered with, whereas this doesn’t

## Types

This package is fully typed with [TypeScript][].
It exports the additional type [`ErrnoException`][errnoexception].

## Compatibility

This package is at least compatible with all maintained versions of Node.js.
As of now, that is Node.js 16 and later.

## Contribute

Yes please!
See [How to Contribute to Open Source][contribute].

## License

[MIT][license] © [Titus Wormer][author] and Node.js contributors

<!-- Definitions -->

[build-badge]: https://github.com/wooorm/import-meta-resolve/workflows/main/badge.svg

[build]: https://github.com/wooorm/import-meta-resolve/actions

[coverage-badge]: https://img.shields.io/codecov/c/github/wooorm/import-meta-resolve.svg

[coverage]: https://codecov.io/github/wooorm/import-meta-resolve

[downloads-badge]: https://img.shields.io/npm/dm/import-meta-resolve.svg

[downloads]: https://www.npmjs.com/package/import-meta-resolve

[npm]: https://docs.npmjs.com/cli/install

[license]: license

[author]: https://wooorm.com

[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c

[typescript]: https://www.typescriptlang.org

[contribute]: https://opensource.guide/how-to-contribute/

[algo]: https://nodejs.org/dist/latest-v14.x/docs/api/esm.html#esm_resolver_algorithm

[native-resolve]: https://nodejs.org/api/esm.html#esm_import_meta_resolve_specifier_parent

[resolve]: #resolvespecifier-parent

[moduleresolve]: #moduleResolvespecifier-parent-conditions-preserveSymlinks

[errnoexception]: #errnoexception
