bidirectional-resolve
Version:
Resolve a package entry point to a file path (like require.resolve/import.meta.resolve) OR a file path to a package entry point
343 lines • 12.7 kB
TypeScript
export type ConditionsOption = {
/**
* Conditions to recursively match against. If none of the listed conditions
* can be found and there are no matching `default` conditions, this function
* returns an empty array.
*
* In addition to `default` (which is always implicitly enabled), the
* following are standard/well-known conditions:
* - `import`
* - `require`
* - `node`
* - `node-addons`
* - `types`
* - `deno`
* - `browser`
* - `react-native`
* - `electron`
* - `development`
* - `production`
*
* Array order does not matter. Priority is determined by the property order
* of conditions defined within a `package.json` `imports`/`exports` mapping.
*
* @see https://nodejs.org/api/packages.html#community-conditions-definitions
*/
conditions?: string[];
};
export type FlattenedImportsOption = {
/**
* The `package.json` `imports` object as a flattened array. Such an array is
* returned by {@link flattenPackageJsonSubpathMap}.
*/
flattenedImports: SubpathMappings;
};
export type FlattenedExportsOption = {
/**
* The `package.json` `exports` object as a flattened array. Such an array is
* returned by {@link flattenPackageJsonSubpathMap}.
*/
flattenedExports: SubpathMappings;
};
export type UnsafeFallbackOption = {
/**
* When encountering a fallback array (i.e. targets present at some level
* within an array),
* [Node.js](https://github.com/nodejs/node/issues/37928#issuecomment-808833604)
* will [select the first valid defined non-null target and ignore all the
* others](https://github.com/nodejs/node/blob/a9cdeeda880a56de6dad10b24b3bfa45e2cccb5d/lib/internal/modules/esm/resolve.js#L417-L432),
* even if that target ends up being unresolvable. However, some build tools
* like [Webpack](https://webpack.js.org/guides/package-exports/#alternatives)
* will evaluate _all_ targets in the fallback array until it encounters one
* that exists on the filesystem. Since this behavior deviates from Node.js
* and hence "the spec," it is considered _unsafe_.
*
* Therefore, by default, this function will ignore all but the very first
* defined non-null target in a fallback array regardless of if it exists on
* the filesystem or not. If no such target is encountered, the final target
* in the fallback array is returned regardless of its value.
*
* Set `includeUnsafeFallbackTargets` to `true` to exhaustively consider _all_
* non-null fallback targets instead, which is a marked deviation from
* Node.js's behavior.
*
* @default false
*/
includeUnsafeFallbackTargets?: boolean;
};
export type ReplaceSubpathAsterisksOption = {
/**
* When returning a subpath pattern, i.e. a subpath containing an asterisk
* ("*"), the asterisks will be replaced by the matching portions of `target` if
* `replaceSubpathAsterisks` is `true`. Otherwise, the literal subpath pattern
* will be returned with asterisk included.
*
* Note that, if `target` contains an asterisk, the literal subpath pattern
* will always be returned regardless of the value of this option.
*
* @default true
*/
replaceSubpathAsterisks?: boolean;
};
/**
* A single flattened subpath in a `package.json` `exports`/`imports` map along
* with its target, matchable conditions, and other metadata. One or more
* subpath mappings together form an imports/exports "entry point" or
* "specifier".
*/
export type SubpathMapping = {
/**
* The subpath that maps to `target`, e.g.:
*
* @example
* ```json
* {
* "exports": {
* "subpath": "target"
* }
* }
* ```
*
* If `isSugared` is `true`, `subpath` is
* [sugared](https://nodejs.org/api/packages.html#exports-sugar) and thus does
* not exist as a property in the actual `package.json` file. `subpath`, if it
* contains at most one asterisk ("*"), becomes a [subpath
* pattern](https://nodejs.org/docs/latest-v19.x/api/packages.html#subpath-patterns).
*/
subpath: string;
/**
* The path to a target file that maps to `subpath`, e.g.:
*
* @example
* ```json
* {
* "exports": {
* "subpath": "target"
* }
* }
* ```
*
* Target may also contain one or more asterisks ("*") only if `subpath` is a
* [subpath
* pattern](https://nodejs.org/docs/latest-v19.x/api/packages.html#subpath-patterns).
*/
target: string | null;
/**
* The combination of resolution conditions that, when matched, result in
* `subpath` resolving to `target`. Conditions are listed in the order they
* are encountered in the map object.
*
* Note that the "default" condition, while present in `conditions`, may not
* actually exist in the actual `package.json` file.
*/
conditions: string[];
/**
* When the subpath mapping is a "default" mapping that occurs after one or
* more sibling conditions, it cannot be selected if one of those siblings is
* selected first. This property contains those sibling conditions that, if
* present, mean this subpath mapping should not be considered.
*
* Useful when reverse-mapping targets to subpaths.
*
* @example
* ```jsonc
* {
* "./strange-subpath": {
* "default": {
* "import": "./import.js",
* "node": "./node.js",
* "default": "./default.js" // <- Never chosen if "import" is specified
* }
* }
* }
* ```
*/
excludedConditions: string[];
/**
* If `true`, the value of `subpath` was inferred but no corresponding
* property exists in the actual `package.json` file.
*
* @example
* ```json
* {
* "name": "my-package",
* "exports": "./is-sugared.js"
* }
* ```
*
* In the above example, `subpath` would be `"."` even though it does not
* exist in the actual `package.json` file.
*
* @see https://nodejs.org/api/packages.html#exports-sugar
*/
isSugared: boolean;
/**
* If `true`, `target` is a so-called "fallback target". This means either (1)
* `target` is a member of a fallback array or (2) the parent or ancestor
* object containing `target` is a member of a fallback array. For example:
*
* @example
* ```json
* {
* "name": "my-package",
* "exports": [
* "./target-is-fallback-1.js",
* {
* "require": "./target-is-fallback-2.js",
* "default": "./target-is-fallback-3.js"
* }
* ]
* }
* ```
*
* Note that, due to how fallback arrays work, a fallback `target` may not be
* reachable in any environment or under any circumstances ever even if all
* the conditions match; multiple fallback `target`s might even overlap in
* strange ways that are hard to reason about. [Node.js also ignores all but
* the first valid defined non-null fallback
* target](https://github.com/nodejs/node/blob/a9cdeeda880a56de6dad10b24b3bfa45e2cccb5d/lib/internal/modules/esm/resolve.js#L417-L432).
*
* **It is for these reasons that fallback arrays should be avoided entirely
* in `package.json` files,** especially any sort of complex nested fallback
* configurations. They're really only useful for consumption by build tools
* like Webpack or TypeScript, and even then their utility is limited.
*/
isFallback: boolean;
/**
* When `isFallback` is true, `isFistNonNullFallback` will be `true` if
* `target` is the first non-`null` member in the flattened fallback array.
*/
isFirstNonNullFallback: boolean;
/**
* When `isFallback` is true, `isLastFallback` will be `true` if `target` is
* the last member in the flattened fallback array regardless of value of
* `target`.
*/
isLastFallback: boolean;
/**
* If `true`, this condition is guaranteed to be impossible to reach, likely
* because it occurs after the "default" condition.
*/
isDeadCondition: boolean;
};
/**
* A flat array of subpath-target mappings enumerating all potential
* `exports`/`imports` entry points within a `package.json` file.
*/
export type SubpathMappings = SubpathMapping[];
/**
* Given `target` and `conditions`, this function returns an array of zero or
* more entry points that are guaranteed to resolve to `target` when the exact
* `conditions` are active in the runtime. This is done by reverse-mapping
* `target` using `exports` from `package.json`. `exports` is assumed to be
* valid.
*
* Entry points are sorted in the order they're encountered with the caveat that
* exact subpaths always come before subpath patterns. Note that, if `target`
* contains one or more asterisks, the subpaths returned by this function will
* also contain an asterisk. The only other time this function returns a subpath
* with an asterisk is if the subpath is a "many-to-one" mapping; that is: the
* subpath has an asterisk but its target does not. For instance:
*
* @example
* ```json
* {
* "exports": {
* "many-to-one-subpath-returned-with-asterisk-1/*": "target-with-no-asterisk.js",
* "many-to-one-subpath-returned-with-asterisk-2/*": null,
* }
* }
* ```
*
* In this case, the asterisk can be replaced with literally anything and it
* would still match. Hence, the replacement is left up to the caller.
*/
export declare function resolveEntryPointsFromExportsTarget({
flattenedExports,
target,
conditions,
includeUnsafeFallbackTargets,
replaceSubpathAsterisks
}: {
/**
* The target that will be reverse-mapped to zero or more subpaths from the
* `package.json` `exports` object.
*/
target: string | null;
} & FlattenedExportsOption & ConditionsOption & UnsafeFallbackOption & ReplaceSubpathAsterisksOption): string[];
/**
* Given `entryPoint` and `conditions`, this function returns an array of zero
* or more targets that `entryPoint` is guaranteed to resolve to when the exact
* `conditions` are active in the runtime. This is done by mapping `entryPoint`
* using `exports` from `package.json`. `exports` is assumed to be valid.
*/
export declare function resolveExportsTargetsFromEntryPoint({
flattenedExports,
entryPoint,
conditions,
includeUnsafeFallbackTargets
}: {
/**
* The entry point that will be mapped to zero or more targets from the
* `package.json` `exports` object.
*/
entryPoint: string;
} & FlattenedExportsOption & ConditionsOption & UnsafeFallbackOption): string[];
/**
* Given `target` and `conditions`, this function returns an array of zero or
* more entry points that are guaranteed to resolve to `target` when the exact
* `conditions` are active in the runtime. This is done by reverse-mapping
* `target` using `imports` from `package.json`. `imports` is assumed to be
* valid.
*
* Entry points are sorted in the order they're encountered with the caveat that
* exact subpaths always come before subpath patterns. Note that, if `target`
* contains one or more asterisks, the subpaths returned by this function will
* also contain an asterisk. The only other time this function returns a subpath
* with an asterisk is if the subpath is a "many-to-one" mapping; that is: the
* subpath has an asterisk but its target does not. For instance:
*
* @example
* ```json
* {
* "imports": {
* "many-to-one-subpath-returned-with-asterisk-1/*": "target-with-no-asterisk.js",
* "many-to-one-subpath-returned-with-asterisk-2/*": null,
* }
* }
* ```
*
* In this case, the asterisk can be replaced with literally anything and it
* would still match. Hence, the replacement is left up to the caller.
*/
export declare function resolveEntryPointsFromImportsTarget({
flattenedImports,
target,
conditions,
includeUnsafeFallbackTargets,
replaceSubpathAsterisks
}: {
/**
* The target that will be reverse-mapped to zero or more subpaths from the
* `package.json` `imports` object.
*/
target: string | null;
} & FlattenedImportsOption & ConditionsOption & UnsafeFallbackOption & ReplaceSubpathAsterisksOption): string[];
/**
* Given `entryPoint` and `conditions`, this function returns an array of zero
* or more targets that `entryPoint` is guaranteed to resolve to when the exact
* `conditions` are active in the runtime. This is done by mapping `entryPoint`
* using `imports` from `package.json`. `imports` is assumed to be valid.
*/
export declare function resolveImportsTargetsFromEntryPoint({
flattenedImports,
entryPoint,
conditions,
includeUnsafeFallbackTargets
}: {
/**
* The entry point that will be mapped to zero or more targets from the
* `package.json` `imports` object.
*/
entryPoint: string;
} & FlattenedImportsOption & ConditionsOption & UnsafeFallbackOption): string[];