A tiny (613b), correct, general-purpose, and configurable `"imports"` resolver without file-system reliance (forked from @lukeed's resolve.exports)
Installations
npm install @okikio/resolve.imports
Developer
okikio
Developer Guide
Module System
CommonJS, ESM
Min. Node Version
>=10
Typescript Support
Yes
Node Version
18.12.1
NPM Version
8.19.3
Statistics
3 Stars
57 Commits
1 Watching
1 Branches
1 Contributors
Updated on 12 Mar 2023
Languages
JavaScript (100%)
Total Downloads
Cumulative downloads
Total Downloads
1,025
Last day
0%
1
Compared to previous day
Last week
100%
2
Compared to previous week
Last month
-62.1%
11
Compared to previous month
Last year
-82.5%
153
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
@okikio/resolve.imports
A tiny (613b), correct, general-purpose, and configurable subpath
"imports"
resolver without file-system reliance. A fork ofresolve.exports
, but forimports
.
Warning: Unlike
resolve.exports
,@okikio/resolve.imports
doesn't have a default entry. This means that you must be explicit about the subpath to resolve
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 "imports"
resolution, then we're headed for deep trouble. It will make supporting (and using) "imports"
nearly impossible, which may force its abandonment and along with it, its benefits.
Let's have nice things.
TODO
- imports string
- imports object (single entry)
- imports object (multi entry)
- nested / recursive conditions
- imports arrayable
- directory mapping (
#foobar/
=>/foobar/
) - directory mapping (
#foobar/*
=>./other/*.js
) - directory mapping w/ conditions
- directory mapping w/ nested conditions
-
legacy fields (main
vsmodule
vs ...) -
legacy "browser" files object
Install
1$ npm install @okikio/resolve.imports
Usage
Please see
/test/
for examples.
1import { resolve, legacy } from "@okikio/resolve.imports"; 2 3const contents = { 4 name: "foobar", 5 module: "dist/module.mjs", 6 main: "dist/require.js", 7 imports: { 8 "#deps": { 9 import: "./dist/module.mjs", 10 require: "./dist/require.js", 11 }, 12 "#lite": { 13 worker: { 14 browser: "./lite/worker.brower.js", 15 node: "./lite/worker.node.js", 16 }, 17 import: "./lite/module.mjs", 18 require: "./lite/require.js", 19 }, 20 }, 21}; 22 23// be explicit about the subpath to resolve, unlike `resolve.exports` 24// there is no default entry 25resolve(contents, "#lite"); //=> "./lite/module.mjs" 26 27// Assume `require` usage 28resolve(contents, "#deps", { require: true }); //=> "./dist/require.js" 29resolve(contents, "#lite", { require: true }); //=> "./lite/require.js" 30 31// Throws "Missing <entry> export in <name> package" Error 32resolve(contents, "foobar/hello"); 33resolve(contents, "./hello/world"); 34 35// Add custom condition(s) 36resolve(contents, "#lite", { 37 conditions: ["worker"], 38}); // => "./lite/worker.node.js" 39 40// Toggle "browser" condition 41resolve(contents, "#lite", { 42 conditions: ["worker"], 43 browser: true, 44}); // => "./lite/worker.browser.js"
API
resolve(pkg, entry, options?)
Returns: string
or undefined
Traverse the "exports"
within the contents of a package.json
file.
If the contents does not contain an "exports"
map, then undefined
will be returned.
Successful 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.
This function may throw an Error if:
- the requested
entry
cannot be resolved (aka, not defined in the"exports"
map) - an
entry
was resolved but no known conditions were found (seeoptions.conditions
)
pkg
Type: object
Required: true
The package.json
contents.
entry
Type: string
Required: false
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 | Error | be explicit about the subpath import being used |
'#' | './src' | subpath import of '#' |
'foobar' | Error | all imports must be subpath imports starting with '#' |
'#/lite' | './lite' | value was relative |
'#lite' | './lite' | value was not relative & did not have pkg.name prefix |
options.require
Type: boolean
Default: false
When truthy, the "require"
field is added to the list of allowed/known conditions.
When falsey, the "import"
field is added to the list of allowed/known conditions instead.
options.browser
Type: boolean
Default: false
When truthy, the "browser"
field is added to the list of allowed/known conditions.
options.conditions
Type: string[]
Default: []
Provide a list of additional/custom conditions that should be accepted when seen.
Important: The order specified within
options.conditions
does not matter.
The matching order/priority is always determined by the"imports"
map's key order.
For example, you may choose to accept a "production"
condition in certain environments. Given the following pkg
content:
1const contents = { 2 // ... 3 imports: { 4 "#dep": { 5 worker: "./index.worker.js", 6 require: "./index.require.js", 7 production: "./index.prod.js", 8 import: "./index.import.mjs", 9 } 10 }, 11}; 12 13resolve(contents, "#dep"); 14//=> "./index.import.mjs" 15 16resolve(contents, "#dep", { 17 conditions: ["production"], 18}); //=> "./index.prod.js" 19 20resolve(contents, "#dep", { 21 conditions: ["production"], 22 require: true, 23}); //=> "./index.require.js" 24 25resolve(contents, "#dep", { 26 conditions: ["production", "worker"], 27 require: true, 28}); //=> "./index.worker.js" 29 30resolve(contents, "#dep", { 31 conditions: ["production", "worker"], 32}); //=> "./index.worker.js"
options.unsafe
Type: boolean
Default: false
Important: You probably do not want this option!
It will break out of Node's default resolution conditions.
When enabled, this option will ignore all other options except options.conditions
. This is because, when enabled, options.unsafe
does not assume or provide any default conditions except the "default"
condition.
1resolve(contents, "#dep"); 2//=> Conditions: ["default", "import", "node"] 3 4resolve(contents, "#dep", { unsafe: true }); 5//=> Conditions: ["default"] 6 7resolve(contents, "#dep", { unsafe: true, require: true, browser: true }); 8//=> Conditions: ["default"]
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:
1resolve(contents, "#dep", { 2 unsafe: true, 3 conditions: ["require"], 4}); 5//=> Conditions: ["default", "require"] 6 7resolve(contents, "#dep", { 8 unsafe: true, 9 conditions: ["browser", "require", "custom123"], 10}); 11//=> Conditions: ["default", "browser", "require", "custom123"]
License
MIT © Okiki Ojo
No vulnerabilities found.
No security vulnerabilities found.
Other packages similar to @okikio/resolve.imports
resolve.imports
resolve "imports" in package.json
@okikio/sharedworker
A small mostly spec. compliant polyfill/ponyfill for SharedWorkers, it acts as a drop in replacement for normal Workers, and supports an API surface that matches normal Workers.
spring-easing
Quick and easy spring animations. Works with other animation libraries (animejs, framer motion, motion one, @okikio/animate, etc...) or the Web Animation API (WAAPI).
pattern-key-compare
PATTERN_KEY_COMPARE implementation