npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

bare-module

v7.0.3

Published

The module system that powers Bare

Readme

bare-module

The module system that powers Bare. It resolves and loads CommonJS and ECMAScript modules, as well as JSON, native addons, assets, bundles, binary, and text, and implements package.json resolution including the "exports", "imports", and conditional fields. Resolution and loading are driven by pluggable protocols, so modules can be served from somewhere other than the file system, such as a Hyperdrive or a bare-bundle.

npm i bare-module

Usage

A module is loaded by its WHATWG URL. The source may be read through the module's protocol or passed in directly:

const Module = require('bare-module')

// Load a module directly from source, without it existing on disk.
const foo = await Module.load(
  new URL('file:///foo.js'),
  'module.exports = function add (a, b) { return a + b }'
)

foo.exports(2, 3)
// 5

To resolve and load specifiers relative to a directory, as require() does, create a require() bound to a parent URL. The default protocol has no backing store of its own and cannot read from the file system, so pass a protocol that serves the source:

const Module = require('bare-module')

const require = Module.createRequire('file:///directory/', { protocol })

// Resolves and loads `file:///directory/foo.js`, reading it through `protocol`.
const foo = require('./foo.js')

The same machinery backs the require() and import available to modules as they run; see CommonJS modules and ECMAScript modules for what each exposes.

Packages

A package is a directory with a package.json file.

Fields

"name"

{
  "name": "my-package"
}

The name of the package. This is used for addon resolution, self-referencing, and importing packages by name.

"version"

{
  "version": "1.2.3"
}

The current version of the package. This is used for addon resolution.

"type"

{
  "type": "module"
}

The module format used for .js files. If not defined, .js files are interpreted as CommonJS. If set to "module", .js files are instead interpreted as ES modules.

"exports"

{
  "exports": {
    ".": "./index.js"
  }
}

The entry points of the package. If defined, only the modules explicitly exported by the package may be imported when importing the package by name.

Subpath exports

A package may define more than one entry point by declaring several subpaths with the main export being ".":

{
  "exports": {
    ".": "./index.js",
    "./submodule": "./lib/submodule.js"
  }
}

When importing the package by name, require('my-package') will resolve to <modules>/my-package/index.js whereas require('my-package/submodule') will resolve to <modules>/my-package/lib/submodule.js.

Conditional exports

Conditional exports allow packages to provide different exports for different conditions, such as the loading method the importing module uses (e.g. require() vs import):

{
  "exports": {
    ".": {
      "import": "./index.mjs",
      "require": "./index.cjs"
    }
  }
}

When importing the package by name, require('my-package') will resolve to <modules>/my-package/index.cjs whereas import 'my-package' will resolve to <modules>/my-package/index.mjs.

Similarly, conditional exports can be used to provide different entry points for different runtimes:

{
  "exports": {
    ".": {
      "bare": "./bare.js",
      "node": "./node.js"
    }
  }
}

To provide a fallback for when no other conditions match, the "default" condition can be declared:

{
  "exports": {
    ".": {
      "bare": "./bare.js",
      "node": "./node.js",
      "default": "./fallback.js"
    }
  }
}

The following conditions are supported, listed in order from most specific to least specific as conditions should be defined:

| Condition | Description | | :------------- | :---------------------------------------------------------------------------------------------------------------------------------- | | "import" | Matches when the package is loaded via import or import(). | | "require" | Matches when the package is loaded via require(). | | "asset" | Matches when the package is loaded via require.asset(). | | "addon" | Matches when the package is loaded via require.addon(). | | "bare" | Matches for any Bare environment. | | "node" | Matches for any Node.js environment. | | "<platform>" | Matches when equal to Bare.platform. See Bare.platform for possible values. | | "<arch>" | Matches when equal to Bare.arch. See Bare.arch for possible values. | | "simulator" | Matches when Bare was compiled for a simulator. | | "default" | The fallback that always matches. This condition should always be last. |

Export conditions are evaluated in the order they are defined in the "exports" field. This means that less specific conditionals defined first will override more specific conditions define later. For example, the following will always call ./fallback.js because "default" always matches and is defined first.

{
  "exports": {
    ".": {
      "default": "./fallback.js",
      "bare": "./bare.js"
    }
  }
}

This is why the general rule is that conditions should be from most specific to least specific when defined.

Self-referencing

Within a package, exports defined in the "exports" field can be referenced by importing the package by name. For example, given the following package.json...

{
  "name": "my-package",
  "exports": {
    ".": "./index.js",
    "./submodule": "./lib/submodule.js"
  }
}

...any module within my-package may reference these entry points using either require('my-package') or require('my-package/submodule').

Exports sugar

If a package defines only a single export, ".", it may leave out the subpath entirely:

{
  "exports": "./index.js"
}

"imports"

A private mapping for import specifiers within the package itself. Similar to "exports", the "imports" field can be used to conditional import other packages within the package. But unlike "exports", "imports" permits mapping to external packages.

The rules are otherwise analogous to the "exports" field.

Subpath imports

Just like exports, subpaths can be used when importing a module internally.

{
  "imports": {
    ".": "./index.js",
    "./submodule": "./lib/submodule.js"
  }
}
Conditional imports

Adding conditional imports allows importing different packages based on the configured conditions. As an example:

{
  "imports": {
    "bar": {
      "require": "./baz.cjs",
      "import": "./baz.mjs"
    }
  }
}

When importing the package bar as require('bar') will resolve to ./baz.cjs, but when importing with import('bar') will resolve to ./baz.mjs.

To provide a fallback for when no other conditions are met, the "default" condition can be configured like so:

{
  "imports": {
    "bar": {
      "require": "./baz.cjs",
      "asset": "./baz.txt",
      "default": "./baz.mjs"
    }
  }
}

The following conditions are supported, listed in order from most specific to least specific as conditions should be defined:

| Condition | Description | | :------------- | :---------------------------------------------------------------------------------------------------------------------------------- | | "import" | Matches when the package is loaded via import or import(). | | "require" | Matches when the package is loaded via require(). | | "asset" | Matches when the package is loaded via require.asset(). | | "addon" | Matches when the package is loaded via require.addon(). | | "bare" | Matches for any Bare environment. | | "node" | Matches for any Node.js environment. | | "<platform>" | Matches when equal to Bare.platform. See Bare.platform for possible values. | | "<arch>" | Matches when equal to Bare.arch. See Bare.arch for possible values. | | "simulator" | Matches when Bare was compiled for a simulator. | | "default" | The fallback that always matches. This condition should always be last. |

The general rule is that conditions should be from most specific to least specific when defined.

# Prefix

All import maps are private to the package and allow mapping to external packages. Entries in "imports" may start with # to disambiguate from external packages, but it is not required unlike in Node.js.

"engines"

{
  "engines": {
    "bare": ">=1.0.5"
  }
}

The "engines" field defines the engine requirements of the package. During module resolution, the versions declared by Bare.versions will be tested against the requirements declared by the package and resolution fail if they're not satisfied.

API

See the bare-module reference.

CommonJS modules

require(specifier[, options])

Used to import JavaScript or JSON modules and local files. Relative paths such as ./, ./foo, ./bar/baz, and ../foo will be resolved against the directory named by __dirname. POSIX style paths are resolved in an OS independent fashion, meaning that the examples above will work on Windows in the same way they would on POSIX systems.

Returns the exported module contents.

Options include:

options = {
  // The import attributes which instruct how the file or module should be loaded.
  // `type` is one of the strings `script`, `module`, `json`, `bundle`, `addon`,
  // `binary` and `text`. A type that is not one of them is turned down rather
  // than passed over, as is a `type` that is not a string.
  with: { type: 'json' }
}

require.main

The module representing the entry script where the program was launched.

require.cache

The registry of loaded modules for this module's graph, keyed by URL href.

const path = require.resolve(specifier[, parentURL])

Use the internal machinery of require() to resolve the specifier string relative to the URL parentURL and return the path string.

require.addon([specifier][, parentURL])

Also used to import modules but specifically loads only addon modules. specifier is resolved relative to parentURL using the addon resolution algorithm.

Returns the exported module contents.

A common pattern for writing an addon module is to use require.addon() as the JavaScript module exports:

module.exports = require.addon()

See bare-addon for a template of building native addon modules.

require.addon.host

Returns the string representation of the platform and architecture used when resolving addons with the pattern <platform>-<arch>[-<environment>]. Returns the same value as Bare.Addon.host.

const path = require.addon.resolve([specifier][, parentURL])

Resolve the specifier string relative to the URL parentURL as an addon and returns the path string. The specifier is resolved using the addon resolution algorithm.

const path = require.asset(specifier[, parentURL])

Resolve the specifier relative to the parentURL and return the path of the asset as a string.

Can be used to load assets, for example the following loads ./foo.txt from the local files:

const fs = require('bare-fs')
const contents = fs.readFileSync(require.asset('./foo.txt'))

ECMAScript modules

import defaultExport, * as name, { export1, export2 as alias2, ... } from 'specifier' with { type: 'json' }

The static import declaration is used to import read-only live bindings that are exported by another module. The imported bindings are called live bindings because they are updated by the module that exported the binding, but cannot be re-assigned by the importing module. In brief, you can import what is exported from another module.

For more information on import syntax, see MDN.

import.meta.url

The string representation of the URL for the current module.

import.meta.main

A boolean representing whether the current module is the entry script where the program was launched.

import.meta.cache

The registry of loaded modules for this module's graph, keyed by URL href. The same value as require.cache for a CommonJS module of the same graph.

import.meta.dirname

The directory name of the current module.

import.meta.filename

The file name of the current module.

const href = import.meta.resolve(specifier[, parentURL])

A module-relative resolution function which returns the URL string for the module. The specifier is a string which is resolved relative to the parentURL which is a WHATWG URL.

import.meta.addon([specifier][, parentURL])

Also used to import modules but specifically loads only addon modules. specifier is resolved relative to parentURL using the addon resolution algorithm.

Returns the exported module contents.

import.meta.addon.host

Returns the string representation of the platform and architecture used when resolving addons with the pattern <platform>-<arch>[-<environment>]. Returns the same value as Bare.Addon.host.

const href = import.meta.addon.resolve([specifier][, parentURL])

Resolve the specifier string relative to the URL parentURL as an addon and returns the URL string. The specifier is resolved using the addon resolution algorithm.

const href = import.meta.asset(specifier[, parentURL])

Resolve the specifier relative to the parentURL and return the URL of the asset as a string.

Custom require()

Creating a custom require allows one to create a preconfigured require(). This can be useful in scenarios such as a Read-Evaluate-Print-Loop (REPL) where the parent URL is set to a directory so requiring relative paths to work correctly.

const require = Module.createRequire(parentURL[, options])

Options include:

options = {
  // The referring module. Supplies the loader, and with it the default for
  // every option below that the loader carries.
  referrer: null,
  // The assumed type of a module without a type using an ambiguous extension
  // such as `.js`. One of Module.constants. Inherited from `referrer` if it is
  // defined, otherwise defaults to SCRIPT.
  defaultType: Module.constants.SCRIPT,
  // A cache of loaded modules. Inherited from `referrer` only while `protocol`
  // and `builtins` are also inherited, as a cache holds the modules of a single
  // graph; narrowing either starts a graph of its own with a fresh cache. Pass
  // a cache object to share one, including a fresh object to share nothing.
  cache,
  // The ModuleProtocol used to resolve and read modules. Defaults to
  // referrer's protocol if defined, otherwise to a protocol with no backing
  // store.
  protocol,
  // A default "imports" map to apply to all specifiers. An object or omitted,
  // following the same syntax and rules as the "imports" property defined in
  // `package.json`.
  imports,
  // A map of preresolved imports with keys being serialized parent URLs and
  // values being "imports" maps. Follows the cache, and like it is an object or
  // omitted.
  resolutions,
  // A map of builtin module specifiers to loaded modules, or omitted. Only the
  // map's own keys are builtins. Inherited from `referrer` if it is defined,
  // including when `protocol` narrows, so pass this too to narrow what the
  // module reaches.
  builtins
}

A module reaches as far as its protocol and its builtins, and a referrer hands on both. Narrowing one does not narrow the other, so code that should not reach what the referrer reaches must be given both:

// The module reads only through `protocol`, but still reaches every builtin the
// referrer was given.
Module.createRequire(parentURL, { referrer, protocol })

// The module reaches no further than what is passed here.
Module.createRequire(parentURL, { referrer, protocol, builtins })

Protocols

Protocols define how to resolve, access and load modules. Custom protocols can be defined to extend or replace how module are resolved and loaded to support things like loading modules via a Hyperdrive.

When no protocol is passed, modules are read through a bare Module.Protocol. It has no backing store of its own and in particular cannot read from the file system, so it finds nothing; pass a protocol to serve the source.

const protocol = new Module.Protocol(methods, context = null)

Methods include:

methods = {
  // function (url): URL | Promise<URL>
  // A function to post-process a resolved URL before it is used, for example to
  // canonicalize symlinks with `realpath` so a module reached through different
  // symlinks dedupes against its real location. Defaults to the identity. May
  // return a promise to resolve asynchronously.
  resolve,
  // function (url): URL
  // The synchronous variant of `resolve`, used when a graph is linked
  // synchronously. Defaults to calling `resolve` and throwing if it returns a
  // promise.
  resolveSync,
  // function (url): boolean | Promise<boolean>
  // A function that returns whether the URL exists as a boolean. Consulted before
  // `read`, so a candidate that does not exist is never fetched. May return a
  // promise to answer asynchronously.
  exists,
  // function (url): boolean
  // The synchronous variant of `exists`. Defaults to calling `exists` and throwing
  // if it returns a promise.
  existsSync,
  // function (url): string | Buffer | null | Promise<string | Buffer | null>
  // A function that returns the source of a URL as a string or buffer, or `null`
  // if it does not exist. May return a promise to serve the source asynchronously.
  read,
  // function (url): string | Buffer | null
  // The synchronous variant of `read`. Defaults to calling `read` and throwing if
  // it returns a promise.
  readSync,
  // function* (url): Iterable<URL> | AsyncIterable<URL>
  // A generator enumerating the URLs under a prefix, used for asset globbing.
  // Defaults to listing nothing, in which case an asset is the prefix itself as
  // `exists` has it, so a backing store need only provide `list` to support an
  // asset naming a directory. Setting `resolved` on what it returns declares the
  // URLs already resolved, sparing whoever drives the listing a `resolve` for
  // every URL in it.
  list,
  // function* (url): Iterable<URL>
  // The synchronous variant of `list`. Defaults to delegating to `list` and
  // throwing if it returns an asynchronous iterable.
  listSync
}

Each method comes in an asynchronous and a synchronous variant. The asynchronous variants may return a promise (or, for list, an asynchronous iterable) to serve modules asynchronously and are driven by the asynchronous Module statics (Module.load and Module.resolve) and Loader methods. The synchronous *Sync variants are driven by the synchronous entry points (require(), Module.loadSync, Module.resolveSync, loader.linkSync, and loader.importSync) and default to calling their asynchronous counterpart, throwing an UNEXPECTED_PROMISE error if it answers asynchronously. A protocol that supports both may implement the *Sync variants directly; for such a protocol a statically imported module is read through the asynchronous read while a require() with a computed specifier is read through readSync.

protocol[Symbol.for('bare.module.protocol.kind')]

The version of the protocol interface. An instance reports the same version as Module.Protocol.

A protocol is the one capability a module system must be handed, and it travels between module systems that may be different copies of bare-module. A module system therefore checks this version before reading anything through a protocol, and throws a PROTOCOL_INCOMPATIBLE error if it does not match. Use Module.Protocol.isProtocol(value) to make the same check, or read the symbol directly to check against a version of your own.

const extended = protocol.extend(methods)

Return a new ModuleProtocol that overrides the given methods, falling back to this protocol for any method not provided. Each method is passed this protocol as its first argument, ahead of the arguments the method normally takes, so an override can defer to what it extends.

A method and its *Sync variant answer the same question, so overriding either governs both and neither is inherited from this protocol. Overriding the asynchronous variant alone is usually what you want, as the synchronous variant then defaults to calling it. Overriding the synchronous variant alone leaves the asynchronous one at its default, which finds nothing; override both to serve both paths. An extension is therefore never half applied, and a protocol narrowed by one reaches no further than the extension allows on either path:

// Reads nothing outside `/public/`, whether the graph is linked synchronously
// or asynchronously.
const restricted = protocol.extend({
  exists(protocol, url) {
    return url.href.startsWith('file:///public/') && protocol.exists(url)
  },

  read(protocol, url) {
    return url.href.startsWith('file:///public/') ? protocol.read(url) : null
  }
})

Loader

A Loader owns a registry of module records keyed by URL and drives resolution and linking against a protocol. Where the Module statics link and evaluate in a single call, a loader exposes linking and evaluation as separate steps, as well as synchronous variants, so modules can be served from an asynchronous protocol such as one backed by a Hyperdrive.

Linking is split into two phases. First a graph is linked: every module reachable from the entry is read, lexed, and recorded, and its native module is created without running any code. Then it is evaluated: the recorded modules run. All IO happens during linking, so evaluation is synchronous regardless of how the graph was fetched.

const loader = new Module.Loader([options])

Options include:

options = {
  // The ModuleProtocol used to resolve and read modules. Defaults to a
  // protocol with no backing store of its own.
  protocol,
  // A map of builtin module specifiers to their exports, or omitted. Only the
  // map's own keys are builtins; a name reached through its prototype chain is
  // not.
  builtins,
  // The assumed type of a module without a type using an ambiguous extension
  // such as `.js`. One of Module.constants.
  defaultType,
  // A default "imports" map to apply to all specifiers. An object or omitted,
  // following the same syntax and rules as the "imports" property defined in
  // `package.json`.
  imports,
  // The maximum number of module reads to perform concurrently while linking.
  // A non-negative integer, defaulting to `0`, which applies no limit.
  concurrency: 0,
  // The module cache. Pass an object to share one, or omit for a fresh cache
  // scoped to this loader. It is an object or nothing; there is no flag for
  // either sharing or not sharing, because a cache holds the modules of a
  // single graph and which graph that is comes of naming the object. A cache is
  // claimed by the first loader to take it, and a later loader that reads
  // through a different protocol or different builtins is turned down with a
  // CACHE_INCOMPATIBLE error.
  cache,
  // A map of preresolved imports with keys being serialized parent URLs and
  // values being "imports" maps. Like the cache it is an object or omitted, and
  // omitting it gives a fresh map scoped to this loader.
  resolutions
}

const module = await loader.link(entry[, source][, options])

Link the module graph rooted at entry, a WHATWG URL, awaiting each read through the protocol so an asynchronous protocol can serve the source. If source is given, it is used instead of reading entry through the protocol. Returns the entry module, instantiated but not yet evaluated.

const module = loader.linkSync(entry[, source][, options])

The synchronous equivalent of loader.link(). It drives the protocol's synchronous methods (resolveSync, existsSync, readSync, and listSync), which throw an UNEXPECTED_PROMISE error when the protocol can only answer asynchronously.

const exports = await loader.import(entry[, options])

Link and evaluate the graph rooted at entry, returning its exports. Awaits the entry's evaluation, so a top-level await in the entry settles before the exports are returned.

const exports = loader.importSync(entry[, options])

The synchronous equivalent of loader.import(). It cannot await, so a top-level await in the entry is unsupported.

const module = loader.get(url)

Return the module record cached under url, a WHATWG URL, or null if none is loaded.

loader.main

The graph's main module: the first entry linked. null until the first link.

loader.cache

The registry of loaded modules, keyed by URL href.

loader.protocol

The ModuleProtocol modules are resolved and read through, shared with every module of the loader's graph.

Threat model

bare-module is one of the addons Bare compiles into its binary, so it inherits Bare's threat model. See docs/threat-model.md for where this addon sits in it.

License

Apache-2.0