source: frontend/node_modules/resolve.exports/readme.md

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 10.1 KB
Line 
1# resolve.exports [![CI](https://github.com/lukeed/resolve.exports/workflows/CI/badge.svg)](https://github.com/lukeed/resolve.exports/actions) [![codecov](https://codecov.io/gh/lukeed/resolve.exports/branch/master/graph/badge.svg?token=4P7d4Omw2h)](https://codecov.io/gh/lukeed/resolve.exports)
2
3> A tiny (813b), correct, general-purpose, and configurable `"exports"` resolver without file-system reliance
4
5***Why?***
6
7Hopefully, 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.
8
9With the push for ESM, we must be _very_ careful and avoid fragmentation. If we, as a community, begin propagating different _dialects_ of `"exports"` resolution, 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.
10
11Let's have nice things.
12
13***TODO***
14
15- [x] exports string
16- [x] exports object (single entry)
17- [x] exports object (multi entry)
18- [x] nested / recursive conditions
19- [x] exports arrayable
20- [x] directory mapping (`./foobar/` => `/foobar/`)
21- [x] directory mapping (`./foobar/*` => `./other/*.js`)
22- [x] directory mapping w/ conditions
23- [x] directory mapping w/ nested conditions
24- [x] legacy fields (`main` vs `module` vs ...)
25- [x] legacy "browser" files object
26
27## Install
28
29```sh
30$ npm install resolve.exports
31```
32
33## Usage
34
35> Please see [`/test/`](/test) for examples.
36
37```js
38import { resolve, legacy } from 'resolve.exports';
39
40const contents = {
41 "name": "foobar",
42 "module": "dist/module.mjs",
43 "main": "dist/require.js",
44 "exports": {
45 ".": {
46 "import": "./dist/module.mjs",
47 "require": "./dist/require.js"
48 },
49 "./lite": {
50 "worker": {
51 "browser": "./lite/worker.brower.js",
52 "node": "./lite/worker.node.js"
53 },
54 "import": "./lite/module.mjs",
55 "require": "./lite/require.js"
56 }
57 }
58};
59
60// Assumes `.` as default entry
61// Assumes `import` as default condition
62resolve(contents); //=> "./dist/module.mjs"
63
64// entry: nullish === "foobar" === "."
65resolve(contents, 'foobar'); //=> "./dist/module.mjs"
66resolve(contents, '.'); //=> "./dist/module.mjs"
67
68// entry: "foobar/lite" === "./lite"
69resolve(contents, 'foobar/lite'); //=> "./lite/module.mjs"
70resolve(contents, './lite'); //=> "./lite/module.mjs"
71
72// Assume `require` usage
73resolve(contents, 'foobar', { require: true }); //=> "./dist/require.js"
74resolve(contents, './lite', { require: true }); //=> "./lite/require.js"
75
76// Throws "Missing <entry> export in <name> package" Error
77resolve(contents, 'foobar/hello');
78resolve(contents, './hello/world');
79
80// Add custom condition(s)
81resolve(contents, 'foobar/lite', {
82 conditions: ['worker']
83}); // => "./lite/worker.node.js"
84
85// Toggle "browser" condition
86resolve(contents, 'foobar/lite', {
87 conditions: ['worker'],
88 browser: true
89}); // => "./lite/worker.browser.js"
90
91// ---
92// Legacy
93// ---
94
95// prefer "module" > "main" (default)
96legacy(contents); //=> "dist/module.mjs"
97
98// customize fields order
99legacy(contents, {
100 fields: ['main', 'module']
101}); //=> "dist/require.js"
102```
103
104## API
105
106### resolve(pkg, entry?, options?)
107Returns: `string` or `undefined`
108
109Traverse the `"exports"` within the contents of a `package.json` file. <br>
110If the contents _does not_ contain an `"exports"` map, then `undefined` will be returned.
111
112Successful resolutions will always result in a string value. This will be the value of the resolved mapping itself – which means that the output is a relative file path.
113
114This function may throw an Error if:
115
116* the requested `entry` cannot be resolved (aka, not defined in the `"exports"` map)
117* an `entry` _was_ resolved but no known conditions were found (see [`options.conditions`](#optionsconditions))
118
119#### pkg
120Type: `object` <br>
121Required: `true`
122
123The `package.json` contents.
124
125#### entry
126Type: `string` <br>
127Required: `false` <br>
128Default: `.` (aka, root)
129
130The desired target entry, or the original `import` path.
131
132When `entry` _is not_ a relative path (aka, does not start with `'.'`), then `entry` is given the `'./'` prefix.
133
134When `entry` begins with the package name (determined via the `pkg.name` value), then `entry` is truncated and made relative.
135
136When `entry` is already relative, it is accepted as is.
137
138***Examples***
139
140Assume we have a module named "foobar" and whose `pkg` contains `"name": "foobar"`.
141
142| `entry` value | treated as | reason |
143|-|-|-|
144| `null` / `undefined` | `'.'` | default |
145| `'.'` | `'.'` | value was relative |
146| `'foobar'` | `'.'` | value was `pkg.name` |
147| `'foobar/lite'` | `'./lite'` | value had `pkg.name` prefix |
148| `'./lite'` | `'./lite'` | value was relative |
149| `'lite'` | `'./lite'` | value was not relative & did not have `pkg.name` prefix |
150
151
152#### options.require
153Type: `boolean` <br>
154Default: `false`
155
156When truthy, the `"require"` field is added to the list of allowed/known conditions.
157
158When falsey, the `"import"` field is added to the list of allowed/known conditions instead.
159
160#### options.browser
161Type: `boolean` <br>
162Default: `false`
163
164When truthy, the `"browser"` field is added to the list of allowed/known conditions.
165
166#### options.conditions
167Type: `string[]` <br>
168Default: `[]`
169
170Provide a list of additional/custom conditions that should be accepted when seen.
171
172> **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.
173
174For example, you may choose to accept a `"production"` condition in certain environments. Given the following `pkg` content:
175
176```js
177const contents = {
178 // ...
179 "exports": {
180 "worker": "./index.worker.js",
181 "require": "./index.require.js",
182 "production": "./index.prod.js",
183 "import": "./index.import.mjs",
184 }
185};
186
187resolve(contents, '.');
188//=> "./index.import.mjs"
189
190resolve(contents, '.', {
191 conditions: ['production']
192}); //=> "./index.prod.js"
193
194resolve(contents, '.', {
195 conditions: ['production'],
196 require: true,
197}); //=> "./index.require.js"
198
199resolve(contents, '.', {
200 conditions: ['production', 'worker'],
201 require: true,
202}); //=> "./index.worker.js"
203
204resolve(contents, '.', {
205 conditions: ['production', 'worker']
206}); //=> "./index.worker.js"
207```
208
209#### options.unsafe
210Type: `boolean` <br>
211Default: `false`
212
213> **Important:** You probably do not want this option! <br>It will break out of Node's default resolution conditions.
214
215When 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.
216
217```js
218resolve(contents);
219//=> Conditions: ["default", "import", "node"]
220
221resolve(contents, { unsafe: true });
222//=> Conditions: ["default"]
223
224resolve(contents, { unsafe: true, require: true, browser: true });
225//=> Conditions: ["default"]
226```
227
228In 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:
229
230```js
231resolve(contents, {
232 unsafe: true,
233 conditions: ["require"]
234});
235//=> Conditions: ["default", "require"]
236
237resolve(contents, {
238 unsafe: true,
239 conditions: ["browser", "require", "custom123"]
240});
241//=> Conditions: ["default", "browser", "require", "custom123"]
242```
243
244
245### legacy(pkg, options?)
246Returns: `string` or `undefined`
247
248Also 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).
249
250You may customize the field priority via [`options.fields`](#optionsfields).
251
252When a field is found, its value is returned _as written_. <br>
253When 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.
254
255#### options.browser
256Type: `boolean` or `string` <br>
257Default: `false`
258
259When truthy, ensures that the `'browser'` field is part of the acceptable `fields` list.
260
261> **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.
262
263When `true` and `"browser"` is an object, then `legacy()` will return the the entire `"browser"` object.
264
265You 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:
266
267* `false` – if the package author decided a file should be ignored; or
268* your `options.browser` string value – but made relative, if not already
269
270> See the [`"browser" field specification](https://github.com/defunctzombie/package-browser-field-spec) for more information.
271
272#### options.fields
273Type: `string[]` <br>
274Default: `['module', 'main']`
275
276A 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.
277
278By 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.
279
280```js
281const contents = {
282 "name": "...",
283 "worker": "worker.js",
284 "module": "module.mjs",
285 "browser": "browser.js",
286 "main": "main.js",
287}
288
289legacy(contents);
290// fields = [module, main]
291//=> "module.mjs"
292
293legacy(contents, { browser: true });
294// fields = [browser, module, main]
295//=> "browser.mjs"
296
297legacy(contents, {
298 fields: ['missing', 'worker', 'module', 'main']
299});
300// fields = [missing, worker, module, main]
301//=> "worker.js"
302
303legacy(contents, {
304 fields: ['missing', 'worker', 'module', 'main'],
305 browser: true,
306});
307// fields = [browser, missing, worker, module, main]
308//=> "browser.js"
309
310legacy(contents, {
311 fields: ['module', 'browser', 'main'],
312 browser: true,
313});
314// fields = [module, browser, main]
315//=> "module.mjs"
316```
317
318## License
319
320MIT © [Luke Edwards](https://lukeed.com)
Note: See TracBrowser for help on using the repository browser.