Hopefully, this module may serve as a reference point (and/or be used directly) so that the varying tools and bundlers within the ecosystem can share a common approach with one another **as well as** with the native Node.js implementation.
With the push for ESM, we must be _very_ careful and avoid fragmentation. If we, as a community, begin propagating different _dialects_ of the resolution algorithm, then we're headed for deep trouble. It will make supporting (and using) `"exports"` nearly impossible, which may force its abandonment and along with it, its benefits.
The [`resolve()`](#resolvepkg-entry-options), [`exports()`](#exportspkg-entry-options), and [`imports()`](#importspkg-target-options) functions share similar API signatures:
A convenience helper which automatically reroutes to [`exports()`](#exportspkg-entry-options) or [`imports()`](#importspkg-target-options) depending on the `entry` value.
When unspecified, `entry` defaults to the `"."` identifier, which means that `exports()` will be invoked.
Successful resolutions will always result in a `string` or `string[]` value. This will be the value of the resolved mapping itself – which means that the output is a relative file path.
Traverse the `"imports"` within the contents of a `package.json` file. <br>
If the contents _does not_ contain an `"imports"` map, then `undefined` will be returned.
Successful resolutions will always result in a `string` or `string[]` value. This will be the value of the resolved mapping itself – which means that the output is a relative file path.
This function may throw an Error if:
* the requested `target` cannot be resolved (aka, not defined in the `"imports"` map)
* an `target`_is_ defined but no known conditions were matched (see [`options.conditions`](#optionsconditions))
#### pkg
Type: `object`<br>
Required: `true`
The `package.json` contents.
#### target
Type: `string`<br>
Required: `true`
The target import identifier; for example, `#hash` or `#hash/md5`.
Import specifiers _must_ begin with the `#` character, as required by the resolution specification. However, if `target` begins with the package name (determined by the `pkg.name` value), then `resolve.exports` will trim it from the `target` identifier. For example, `"foobar/#hash/md5"` will be treated as `"#hash/md5"` for the `"foobar"` package.
## Options
The [`resolve()`](#resolvepkg-entry-options), [`imports()`](#importspkg-target-options), and [`exports()`](#exportspkg-entry-options) functions share these options. All properties are optional and you are not required to pass an `options` argument.
Collectively, the `options` are used to assemble a list of [conditions](https://nodejs.org/docs/latest-v18.x/api/packages.html#conditional-exports) that should be activated while resolving your target(s).
> **Note:** Although the Node.js documentation primarily showcases conditions alongside `"exports"` usage, they also apply to `"imports"` maps too. _([example](https://nodejs.org/docs/latest-v18.x/api/packages.html#subpath-imports))_
> **Important:** The order specified within `options.conditions` does not matter. <br>The matching order/priority is **always** determined by the `"exports"` map's key order.
For example, you may choose to accept a `"production"` condition in certain environments. Given the following `pkg` content:
> **Important:** You probably do not want this option! <br>It will break out of Node's default resolution conditions.
When enabled, this option will ignore **all other options** except [`options.conditions`](#optionsconditions). This is because, when enabled, `options.unsafe`**does not** assume or provide any default conditions except the `"default"` condition.
In other words, this means that trying to use `options.require` or `options.browser` alongside `options.unsafe` will have no effect. In order to enable these conditions, you must provide them manually into the `options.conditions` list:
Also included is a "legacy" method for resolving non-`"exports"` package fields. This may be used as a fallback method when for when no `"exports"` mapping is defined. In other words, it's completely optional (and tree-shakeable).
You may customize the field priority via [`options.fields`](#optionsfields).
When a field is found, its value is returned _as written_. <br>
When no fields were found, `undefined` is returned. If you wish to mimic Node.js behavior, you can assume this means `'index.js'`– but this module does not make that assumption for you.
#### options.browser
Type: `boolean` or `string`<br>
Default: `false`
When truthy, ensures that the `'browser'` field is part of the acceptable `fields` list.
> **Important:** If your custom [`options.fields`](#optionsfields) value includes `'browser'`, then _your_ order is respected. <br>Otherwise, when truthy, `options.browser` will move `'browser'` to the front of the list, making it the top priority.
When `true` and `"browser"` is an object, then `legacy()` will return the the entire `"browser"` object.
You may also pass a string value, which will be treated as an import/file path. When this is the case and `"browser"` is an object, then `legacy()` may return:
*`false`– if the package author decided a file should be ignored; or
* your `options.browser` string value – but made relative, if not already
> See the [`"browser" field specification](https://github.com/defunctzombie/package-browser-field-spec) for more information.
#### options.fields
Type: `string[]`<br>
Default: `['module', 'main']`
A list of fields to accept. The order of the array determines the priority/importance of each field, with the most important fields at the beginning of the list.
By default, the `legacy()` method will accept any `"module"` and/or "main" fields if they are defined. However, if both fields are defined, then "module" will be returned.