LogoPear Docs
ReferencesBareModules

bare-module

Module support for JavaScript

stable

bare-module — Module support for JavaScript. It is a native addon and requires Bare >=1.29.4.

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.

API

Module

new Module(url: URL)

Parameters

ParameterTypeDefaultDescription
urlURLThe WHATWG URL identifying the module.

builtins: Builtins

A map of builtin module specifiers mapped to the loaded module.

cache: Cache

A cache of loaded modules for this module. Defaults to Module.cache.

conditions: Conditions

An array of conditions used to resolve dependencies while loading the module. See Conditional exports for possible values.

defaultType: number

The assumed type of a module without a type using an ambiguous extension, such as .js. See Module.constants.types for possible values.

dirname: string

The directory portion of module.url.

exports: unknown

The exports from the module.

filename: string

The file portion of module.url.

id: string

imports: ImportsMap

The import map when the module was loaded.

main: Module

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

Module.asset(specifier: string, parentURL: URL, opts?: Options): URL

Get the asset URL by resolving specifier relative to parentURL. specifier is a string and parentURL is a WHATWG URL.

Parameters

ParameterTypeDefaultDescription
specifierstringThe asset specifier to resolve.
parentURLURLThe WHATWG URL to resolve specifier relative to.
opts?OptionsResolution options.

Returns URL — The WHATWG URL of the resolved asset.

Throws

  • ASSET_NOT_FOUND — no asset matching specifier could be found relative to parentURL.
  • TypeErrorspecifier is not a string.

Module.builtinModules: Module[]

Always an empty array; provided for Node.js compatibility.

Module.cache: Cache

Module.constants

Module.constants: {
  states: {
    EVALUATED: number
    SYNTHESIZED: number
    RUN: number
  }
  types: {
    SCRIPT: number
    MODULE: number
    JSON: number
    BUNDLE: number
    ADDON: number
    BINARY: number
    TEXT: number
    ASSET: number
  }
}

Constants describing module states (EVALUATED, SYNTHESIZED, RUN) and module types (SCRIPT, MODULE, JSON, BUNDLE, ADDON, BINARY, TEXT, ASSET).

Module.createRequire(parentURL: string | URL, opts?: CreateRequireOptions): Require

Create a preconfigured require() bound to parentURL, so specifiers resolve and load relative to it.

Parameters

ParameterTypeDefaultDescription
parentURLstring | URLThe parent URL that the returned require() resolves and loads specifiers relative to.
opts?CreateRequireOptionsOptions for the created require(), such as its protocol and cache.

Returns Require — A require() bound to parentURL, with main, cache, resolve, addon, and asset attached.

Module.isBuiltin(): boolean

Always returns false; provided for Node.js compatibility.

Module.load(url: URL, opts: LoadOptions): Module

Load a module with the provided url. url is a WHATWG URL. If provided, the source will be passed to the matching extension for the url.

Overloads:

Module.load(url: URL, opts: LoadOptions): Module
Module.load(url: URL, source?: Buffer | string | Bundle | null, opts?: LoadOptions): Module

Parameters

ParameterTypeDefaultDescription
urlURLThe WHATWG URL of the module to load.
optsLoadOptionsLoad options; may carry a source to load directly instead of reading it through the protocol.

Returns Module — The loaded Module, reusing the cached instance if url was already loaded.

Throws

  • TYPE_INCOMPATIBLE — a module is already cached for url with a type incompatible with the requested type.

Module.protocol: Protocol

The ModuleProtocol class for resolving, reading and loading modules. See Protocols for usage.

Module.resolve(specifier: string, parentURL: URL, opts?: ResolveOptions): URL

Resolve the module specifier relative to the parentURL. specifier is a string and parentURL is a WHATWG URL.

Parameters

ParameterTypeDefaultDescription
specifierstringThe module specifier to resolve.
parentURLURLThe WHATWG URL to resolve specifier relative to.
opts?ResolveOptionsResolution options.

Returns URL — The WHATWG URL that specifier resolves to.

Throws

  • MODULE_NOT_FOUND — no module matching specifier could be found relative to parentURL.
  • TypeErrorspecifier is not a string.

path: string

protocol: Protocol

The ModuleProtocol class for resolving, reading and loading modules. See Protocols for usage.

resolutions: ResolutionsMap

A map of preresolved imports with keys being serialized parent URLs and values being "imports" maps.

type: number

The type of the module. See Module.constants.types for possible values.

url: URL

The WHATWG URL identifier of the module.

ModuleProtocol

new ModuleProtocol(methods?: Partial<ModuleProtocol>, context?: ModuleProtocol)

Defines how modules are resolved, read and loaded; custom protocols can serve modules from outside the file system, such as a Hyperdrive or a bare-bundle.

Parameters

ParameterTypeDefaultDescription
methods?Partial<ModuleProtocol>Protocol method overrides; any of preresolve, postresolve, resolve, exists, read, addon, or asset.
context?ModuleProtocolAn existing protocol to fall back to for any method not provided in methods.

addon(url: URL): URL

Post-process URLs for addons before postresolve().

Parameters

ParameterTypeDefaultDescription
urlURLThe resolved addon URL to post-process.

asset(url: URL): URL

Post-process URLs for assets before postresolve().

Parameters

ParameterTypeDefaultDescription
urlURLThe resolved asset URL to post-process.

exists(url: URL, type: number): boolean

Return whether the URL exists.

Parameters

ParameterTypeDefaultDescription
urlURLThe URL to check for existence.
typenumberThe module type being probed (see Module.constants.types).

extend(methods: Partial<ModuleProtocol>): ModuleProtocol

Create a new protocol that uses this protocol as its context, overriding the given methods.

Parameters

ParameterTypeDefaultDescription
methodsPartial<ModuleProtocol>Protocol method overrides for the new protocol.

Returns ModuleProtocol — A new ModuleProtocol that uses this protocol as its context, with methods overriding.

postresolve(url: URL): URL

Process the resolved URL; can be used to convert file paths, etc.

Parameters

ParameterTypeDefaultDescription
urlURLThe resolved URL to post-process.

Returns URL — The (possibly transformed) resolved URL.

preresolve(specifier: string, parentURL: URL): string

Preprocess the specifier and parentURL before the resolve algorithm is called.

Parameters

ParameterTypeDefaultDescription
specifierstringThe module specifier being resolved.
parentURLURLThe URL the specifier is resolved relative to.

Returns string — The (possibly rewritten) specifier to pass into the resolve algorithm.

read(url: URL): Buffer | string | null

Return the source code of a URL, represented as a string or buffer.

Parameters

ParameterTypeDefaultDescription
urlURLThe URL to read.

Returns Buffer | string | null — The source of url as a Buffer or string, or null if it does not exist.

resolve(specifier: string, parentURL: URL, imports: ImportsMap): URL

Resolve the specifier to a URL.

Parameters

ParameterTypeDefaultDescription
specifierstringThe module specifier to resolve.
parentURLURLThe URL to resolve specifier relative to.
importsImportsMapThe "imports" map to apply during resolution.

Types

Attributes

interface Attributes {
  type: Lowercase<keyof typeof constants.types>
}

Import attributes instructing how a module should be loaded.

Cache

interface Cache {
}

A map of module URL hrefs to loaded modules.

Options

interface Options {
  attributes?: Attributes
  builtins?: Builtins
  cache?: Cache
  conditions?: Conditions
  defaultType?: number
  imports?: ImportsMap
  main?: Module
  protocol?: Protocol
  referrer?: Module
  resolutions?: ResolutionsMap
  type?: number
}

LoadOptions

interface LoadOptions {
  isDynamicImport?: boolean
  isImport?: boolean
  attributes?: Attributes
  builtins?: Builtins
  cache?: Cache
  conditions?: Conditions
  defaultType?: number
  imports?: ImportsMap
  main?: Module
  protocol?: Protocol
  referrer?: Module
  resolutions?: ResolutionsMap
  type?: number
}

ResolveOptions

interface ResolveOptions {
  isImport?: boolean
  attributes?: Attributes
  builtins?: Builtins
  cache?: Cache
  conditions?: Conditions
  defaultType?: number
  imports?: ImportsMap
  main?: Module
  protocol?: Protocol
  referrer?: Module
  resolutions?: ResolutionsMap
  type?: number
}

CreateRequireOptions

interface CreateRequireOptions {
  module?: Module
  attributes?: Attributes
  builtins?: Builtins
  cache?: Cache
  conditions?: Conditions
  defaultType?: number
  imports?: ImportsMap
  main?: Module
  protocol?: Protocol
  referrer?: Module
  resolutions?: ResolutionsMap
  type?: number
}

RequireOptions

interface RequireOptions {
  with?: Attributes
}

Options for require(); with holds the import attributes.

RequireAddon

interface RequireAddon {
  host: string
  resolve: (specifier: string, parentURL?: URL) => unknown
}

The require.addon function: imports addon modules, with host and resolve attached.

Require

interface Require {
  main: Module
  cache: Cache
  resolve: (specifier: string, parentURL?: URL) => string
  addon: RequireAddon
  asset: (specifier: string, parentURL?: URL) => string
}

The function returned by Module.createRequire(): resolves and loads modules relative to its parent URL, with main, cache, resolve, addon, and asset attached.

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 (for example 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:

ConditionDescription
"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:

ConditionDescription
"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.

See also

On this page