more
This commit is contained in:
dopeuni444
2025-07-31 12:23:33 +04:00
parent 20b46678b7
commit b5a22951ae
3401 changed files with 331100 additions and 0 deletions

View File

@@ -0,0 +1 @@
function e(e,n,r){throw new Error(r?`No known conditions for "${n}" specifier in "${e}" package`:`Missing "${n}" specifier in "${e}" package`)}function n(n,i,o,f){let s,u,l=r(n,o),c=function(e){let n=new Set(["default",...e.conditions||[]]);return e.unsafe||n.add(e.require?"require":"import"),e.unsafe||n.add(e.browser?"browser":"node"),n}(f||{}),a=i[l];if(void 0===a){let e,n,r,t;for(t in i)n&&t.length<n.length||("/"===t[t.length-1]&&l.startsWith(t)?(u=l.substring(t.length),n=t):t.length>1&&(r=t.indexOf("*",1),~r&&(e=RegExp("^"+t.substring(0,r)+"(.*)"+t.substring(1+r)+"$").exec(l),e&&e[1]&&(u=e[1],n=t))));a=i[n]}return a||e(n,l),s=t(a,c),s||e(n,l,1),u&&function(e,n){let r,t=0,i=e.length,o=/[*]/g,f=/[/]$/;for(;t<i;t++)e[t]=o.test(r=e[t])?r.replace(o,n):f.test(r)?r+n:r}(s,u),s}function r(e,n,r){if(e===n||"."===n)return".";let t=e+"/",i=t.length,o=n.slice(0,i)===t,f=o?n.slice(i):n;return"#"===f[0]?f:o||!r?"./"===f.slice(0,2)?f:"./"+f:f}function t(e,n,r){if(e){if("string"==typeof e)return r&&r.add(e),[e];let i,o;if(Array.isArray(e)){for(o=r||new Set,i=0;i<e.length;i++)t(e[i],n,o);if(!r&&o.size)return[...o]}else for(i in e)if(n.has(i))return t(e[i],n,r)}}function i(e,n={}){let t,i=0,o=n.browser,f=n.fields||["module","main"],s="string"==typeof o;for(o&&!f.includes("browser")&&(f.unshift("browser"),s&&(o=r(e.name,o,!0)));i<f.length;i++)if(t=e[f[i]]){if("string"==typeof t);else{if("object"!=typeof t||"browser"!=f[i])continue;if(s&&(t=t[o],null==t))return o}return"string"==typeof t?"./"+t.replace(/^\.?\//,""):t}}function o(e,r,t){let i,o=e.exports;if(o){if("string"==typeof o)o={".":o};else for(i in o){"."!==i[0]&&(o={".":o});break}return n(e.name,o,r||".",t)}}function f(e,r,t){if(e.imports)return n(e.name,e.imports,r,t)}function s(e,n,t){return"#"===(n=r(e.name,n||"."))[0]?f(e,n,t):o(e,n,t)}exports.exports=o;exports.imports=f;exports.legacy=i;exports.resolve=s;

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,50 @@
{
"version": "2.0.3",
"name": "resolve.exports",
"repository": "lukeed/resolve.exports",
"description": "A tiny (952b), correct, general-purpose, and configurable \"exports\" and \"imports\" resolver without file-system reliance",
"module": "dist/index.mjs",
"main": "dist/index.js",
"types": "index.d.ts",
"license": "MIT",
"author": {
"name": "Luke Edwards",
"email": "luke.edwards05@gmail.com",
"url": "https://lukeed.com"
},
"engines": {
"node": ">=10"
},
"scripts": {
"build": "bundt -m",
"types": "tsc --noEmit",
"test": "uvu -r tsm test"
},
"files": [
"*.d.ts",
"dist"
],
"exports": {
".": {
"types": "./index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./package.json": "./package.json"
},
"keywords": [
"esm",
"exports",
"esmodules",
"fields",
"modules",
"resolution",
"resolve"
],
"devDependencies": {
"bundt": "next",
"tsm": "2.3.0",
"typescript": "4.9.4",
"uvu": "0.5.4"
}
}

View File

@@ -0,0 +1,273 @@
# resolve.exports [![CI](https://github.com/lukeed/resolve.exports/workflows/CI/badge.svg)](https://github.com/lukeed/resolve.exports/actions) [![licenses](https://licenses.dev/b/npm/resolve.exports)](https://licenses.dev/npm/resolve.exports) [![codecov](https://codecov.io/gh/lukeed/resolve.exports/branch/master/graph/badge.svg?token=4P7d4Omw2h)](https://codecov.io/gh/lukeed/resolve.exports)
> A tiny (952b), correct, general-purpose, and configurable `"exports"` and `"imports"` resolver without file-system reliance
***Why?***
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.
Let's have nice things.
## Install
```sh
$ npm install resolve.exports
```
## Usage
> Please see [`/test/`](/test) for examples.
```js
import * as resolve from 'resolve.exports';
// package.json contents
const pkg = {
"name": "foobar",
"module": "dist/module.mjs",
"main": "dist/require.js",
"imports": {
"#hash": {
"import": {
"browser": "./hash/web.mjs",
"node": "./hash/node.mjs",
},
"default": "./hash/detect.js"
}
},
"exports": {
".": {
"import": "./dist/module.mjs",
"require": "./dist/require.js"
},
"./lite": {
"worker": {
"browser": "./lite/worker.browser.js",
"node": "./lite/worker.node.js"
},
"import": "./lite/module.mjs",
"require": "./lite/require.js"
}
}
};
// ---
// Exports
// ---
// entry: "foobar" === "." === default
// conditions: ["default", "import", "node"]
resolve.exports(pkg);
resolve.exports(pkg, '.');
resolve.exports(pkg, 'foobar');
//=> ["./dist/module.mjs"]
// entry: "foobar/lite" === "./lite"
// conditions: ["default", "import", "node"]
resolve.exports(pkg, 'foobar/lite');
resolve.exports(pkg, './lite');
//=> ["./lite/module.mjs"]
// Enable `require` condition
// conditions: ["default", "require", "node"]
resolve.exports(pkg, 'foobar', { require: true }); //=> ["./dist/require.js"]
resolve.exports(pkg, './lite', { require: true }); //=> ["./lite/require.js"]
// Throws "Missing <entry> specifier in <name> package" Error
resolve.exports(pkg, 'foobar/hello');
resolve.exports(pkg, './hello/world');
// Add custom condition(s)
// conditions: ["default", "worker", "import", "node"]
resolve.exports(pkg, 'foobar/lite', {
conditions: ['worker']
}); //=> ["./lite/worker.node.js"]
// Toggle "browser" condition
// conditions: ["default", "worker", "import", "browser"]
resolve.exports(pkg, 'foobar/lite', {
conditions: ['worker'],
browser: true
}); //=> ["./lite/worker.browser.js"]
// Disable non-"default" condition activate
// NOTE: breaks from Node.js default behavior
// conditions: ["default", "custom"]
resolve.exports(pkg, 'foobar/lite', {
conditions: ['custom'],
unsafe: true,
});
//=> Error: No known conditions for "./lite" specifier in "foobar" package
// ---
// Imports
// ---
// conditions: ["default", "import", "node"]
resolve.imports(pkg, '#hash');
resolve.imports(pkg, 'foobar/#hash');
//=> ["./hash/node.mjs"]
// conditions: ["default", "import", "browser"]
resolve.imports(pkg, '#hash', { browser: true });
resolve.imports(pkg, 'foobar/#hash');
//=> ["./hash/web.mjs"]
// conditions: ["default"]
resolve.imports(pkg, '#hash', { unsafe: true });
resolve.imports(pkg, 'foobar/#hash');
//=> ["./hash/detect.mjs"]
resolve.imports(pkg, '#hello/world');
resolve.imports(pkg, 'foobar/#hello/world');
//=> Error: Missing "#hello/world" specifier in "foobar" package
// ---
// Legacy
// ---
// prefer "module" > "main" (default)
resolve.legacy(pkg); //=> "dist/module.mjs"
// customize fields order
resolve.legacy(pkg, {
fields: ['main', 'module']
}); //=> "dist/require.js"
```
## API
The [`resolve()`](#resolvepkg-entry-options), [`exports()`](#exportspkg-entry-options), and [`imports()`](#importspkg-target-options) functions share similar API signatures:
```ts
export function resolve(pkg: Package, entry?: string, options?: Options): string[] | undefined;
export function exports(pkg: Package, entry?: string, options?: Options): string[] | undefined;
export function imports(pkg: Package, target: string, options?: Options): string[] | undefined;
// ^ not optional!
```
All three:
* accept a `package.json` file's contents as a JSON object
* accept a target/entry identifier
* may accept an [Options](#options) object
* return `string[]`, `string`, or `undefined`
The only difference is that `imports()` must accept a target identifier as there can be no inferred default.
See below for further API descriptions.
> **Note:** There is also a [Legacy Resolver API](#legacy-resolver)
---
### resolve(pkg, entry?, options?)
Returns: `string[]` or `undefined`
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.
```js
import * as r from 'resolve.exports';
let pkg = {
name: 'foobar',
// ...
};
r.resolve(pkg);
//~> r.exports(pkg, '.');
r.resolve(pkg, 'foobar');
//~> r.exports(pkg, '.');
r.resolve(pkg, 'foobar/subpath');
//~> r.exports(pkg, './subpath');
r.resolve(pkg, '#hash/md5');
//~> r.imports(pkg, '#hash/md5');
r.resolve(pkg, 'foobar/#hash/md5');
//~> r.imports(pkg, '#hash/md5');
```
### exports(pkg, entry?, options?)
Returns: `string[]` or `undefined`
Traverse the `"exports"` within the contents of a `package.json` file. <br>
If the contents _does not_ contain an `"exports"` 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 `entry` cannot be resolved (aka, not defined in the `"exports"` map)
* an `entry` _is_ defined but no known conditions were matched (see [`options.conditions`](#optionsconditions))
#### pkg
Type: `object` <br>
Required: `true`
The `package.json` contents.
#### entry
Type: `string` <br>
Required: `false` <br>
Default: `.` (aka, root)
The desired target entry, or the original `import` path.
When `entry` _is not_ a relative path (aka, does not start with `'.'`), then `entry` is given the `'./'` prefix.
When `entry` begins with the package name (determined via the `pkg.name` value), then `entry` is truncated and made relative.
When `entry` is already relative, it is accepted as is.
***Examples***
Assume we have a module named "foobar" and whose `pkg` contains `"name": "foobar"`.
| `entry` value | treated as | reason |
|-|-|-|
| `null` / `undefined` | `'.'` | default |
| `'.'` | `'.'` | value was relative |
| `'foobar'` | `'.'` | value was `pkg.name` |
| `'foobar/lite'` | `'./lite'` | value had `pkg.name` prefix |
| `'./lite'` | `'./lite'` | value was relative |
| `'lite'` | `'./lite'` | value was not relative & did not have `pkg.name` prefix |
### imports(pkg, target, options?)
Returns: `string[]` or `undefined`
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