bare-module
The module system that powers Bare
v7.5.0bare-module — The module system that powers Bare. It is a native addon and requires Bare >=1.35.0.
npm i bare-moduleUsage
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)
// 5To 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.
Loading bundles
A .bundle loads as a module graph of its own. Pass the bundle as the source, either as its bytes or, from v7.4.0, as a Bundle that has already been parsed with bare-bundle:
const Module = require('bare-module')
const Bundle = require('bare-bundle')
const bundle = new Bundle()
.write('/foo.js', "module.exports = require('./bar')", { main: true })
.write('/bar.js', 'module.exports = __filename')
const foo = Module.loadSync(new URL('file:///app.bundle'), bundle)
foo.exports
// '/app.bundle/bar.js'The bundle serves the modules under its mount. The mount is the URL of the bundle followed by /, so the bundle in the example serves file:///app.bundle/foo.js and file:///app.bundle/bar.js. Added in v7.3.0: the mount option sets another URL to mount the entry at:
const foo = Module.loadSync(new URL('file:///app.bundle'), bundle, {
mount: 'file:///app/'
})
foo.exports
// '/app/bar.js'mount is a URL or a string that parses as one, and a / is added to the end if it has none. A value that is neither throws a TypeError. Module.load(), Module.loadSync(), and the loader's link(), linkSync(), import(), and importSync() apply it. instantiate() and instantiateSync() take the same options but do not, so a bundle they compile is mounted at its own URL, unless the same loader was already linked with a mount for that URL. From Bare 1.35.0, Bare.Thread forwards its own mount option to Module.loadSync().
- A key that starts with
/, such as/foo.js, is placed under the mount. A key that is already an absolute URL, such asfile:///app/foo.js, stays where it is, so the mount has to cover it. - The bundle serves only the modules under its mount. Any other module is read through the protocol, and resolution fails with
MODULE_NOT_FOUNDif the protocol does not have it. This applies to a key outside the bundle, such as/../bar.js, and to an absolute key that the mount does not cover. Passfile:///as the mount to let a bundle serve any file. - Before v7.2.2 a bundle also served a module outside its mount when the protocol did not have it. A bundle whose absolute keys lie outside its own URL therefore loaded in v7.2.1 and fails from v7.2.2 with
MODULE_NOT_FOUND, unless it is given amountthat covers the keys. The bundle's resolutions for such a module were merged into the graph'sresolutions, and no longer are. mountapplies to the entry only. A bundle that another module reaches withrequire()orimportis mounted at its own URL.- Two bundles loaded with the same
cache, the same protocol, and the same mount share modules. The second gets the modules that the first loaded, even if its own copies differ. Acachethat is read through a different protocol is refused withCACHE_INCOMPATIBLE. - A bundle can leave an addon out and point its resolutions at a file outside the bundle, such as
/../node_modules/bare-tcp/prebuilds/darwin-arm64/bare-tcp.bare, which is whatbare-packwrites with--offload. The addon is looked up through the protocol, so itsexists()must report the file, or loading the bundle fails withMODULE_NOT_FOUND.
Asynchronous protocols
Module.load() and Module.resolve() await a protocol that answers with promises, so modules can be served from a store that is not synchronous, such as a Hyperdrive. Once a graph is linked, require() runs synchronously and asks the protocol through its *Sync methods, which throw UNEXPECTED_PROMISE when the protocol can only answer asynchronously.
From v7.2.2:
- A
require()orrequire.asset()of a specifier that the graph already looked up while linking is answered from that lookup. A module, package, or asset that was missing throwsMODULE_NOT_FOUNDorASSET_NOT_FOUND, where it threwUNEXPECTED_PROMISEbefore. A specifier built at run time, such asrequire('./' + name), was never looked up, so it still throwsUNEXPECTED_PROMISE. - A
.bundleloads through an asynchronous protocol, including one with an offloaded addon and one that a.mjsmodule imports. Before, loading a bundle through one threwUNEXPECTED_PROMISE.
API
Module
new Module(url: URL)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | The WHATWG URL identifying the module. |
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
Module.Condition
type Condition = 'require' | 'import' | 'asset' | 'addon'The import condition a specifier is resolved for.
Module.constants
Module.constants: {
SCRIPT: number
MODULE: number
JSON: number
BUNDLE: number
ADDON: number
BINARY: number
TEXT: number
}Constants describing module types (SCRIPT, MODULE, JSON, BUNDLE, ADDON, BINARY, TEXT).
Module.createRequire(parentURL: string | URL, opts?: CreateRequireOptions): Require
Create a preconfigured require() bound to parentURL, so specifiers resolve and load relative to it.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
parentURL | string | URL | — | The parent URL that the returned require() resolves and loads specifiers relative to. |
opts? | CreateRequireOptions | — | Options 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.
Throws
TypeError—parentURLis neither a string nor a WHATWGURL, and noreferrerwas given.
Module.load(url: URL, opts: LoadOptions): Promise<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. source is a Buffer, a string, or, from v7.4.0, a Bundle that has already been parsed. A Bundle is only accepted for a module that loads as a bundle, such as a .bundle. See Loading bundles.
Overloads:
Module.load(url: URL, opts: LoadOptions): Promise<Module>
Module.load(url: URL, source?: Buffer | Bundle | string | null, opts?: LoadOptions): Promise<Module>Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | The WHATWG URL of the module to load. |
source? | Buffer | Bundle | string | null | — | The source of the module, used instead of reading url through the protocol. A parsed Bundle is accepted from v7.4.0, for a module that loads as a bundle. It is its own argument, as opts does not carry a source. |
opts | LoadOptions | — | Load options. |
Returns Promise<Module> — The loaded Module, reusing the cached instance if url was already loaded.
Throws
TYPE_INCOMPATIBLE— a module is already cached forurlwith a type incompatible with the requestedtype.TypeError—opts.mountis neither aURLnor a string that parses as one. Added in v7.3.0.
Module.loadSync(url: URL, opts: LoadOptions): Module
As load(), but synchronous, for a caller that cannot await one. The protocol is driven through its *Sync methods and so must answer synchronously. A module with a top-level await is returned as soon as its evaluation is started rather than once it has finished, as there is nothing to wait on it here.
Overloads:
Module.loadSync(url: URL, opts: LoadOptions): Module
Module.loadSync(url: URL, source?: Buffer | Bundle | string | null, opts?: LoadOptions): ModuleParameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | The WHATWG URL of the module to load. |
source? | Buffer | Bundle | string | null | — | The source of the module, used instead of reading url through the protocol. A parsed Bundle is accepted from v7.4.0, for a module that loads as a bundle. It is its own argument, as opts does not carry a source. |
opts | LoadOptions | — | Load options. |
Returns Module — The loaded Module, reusing the cached instance if url was already loaded.
Throws
UNEXPECTED_PROMISE— the protocol answered asynchronously.TYPE_INCOMPATIBLE— a module is already cached forurlwith a type incompatible with the requestedtype.TypeError—opts.mountis neither aURLnor a string that parses as one. Added in v7.3.0.
Module.resolve(specifier: string, parentURL: URL, opts?: ResolveOptions): Promise<URL>
Resolve the module specifier relative to the parentURL. specifier is a string and parentURL is a WHATWG URL. condition is an optional import condition, defaulting to 'require'; pass 'asset' to resolve an asset rather than a module.
Overloads:
Module.resolve(specifier: string, parentURL: URL, opts?: ResolveOptions): Promise<URL>
Module.resolve(specifier: string, parentURL: URL, condition: Condition, opts?: ResolveOptions): Promise<URL>Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
specifier | string | — | The module specifier to resolve. |
parentURL | URL | — | The WHATWG URL to resolve specifier relative to. |
opts? | ResolveOptions | — | Resolution options. |
Returns Promise<URL> — The WHATWG URL that specifier resolves to.
Throws
MODULE_NOT_FOUND— no module matchingspecifiercould be found relative toparentURL.ASSET_NOT_FOUND— the'asset'condition was used and no matching asset could be found.ADDON_NOT_FOUND— the'addon'condition was used and no matching addon could be found.TypeError—specifieris not a string,parentURLis not a WHATWGURL, orconditionis not a known condition.
Module.resolveSync(specifier: string, parentURL: URL, opts?: ResolveOptions): URL
As resolve(), but synchronous, for a caller that cannot await one. The protocol is driven through its *Sync methods and so must answer synchronously.
Overloads:
Module.resolveSync(specifier: string, parentURL: URL, opts?: ResolveOptions): URL
Module.resolveSync(specifier: string, parentURL: URL, condition: Condition, opts?: ResolveOptions): URLParameters
| Parameter | Type | Default | Description |
|---|---|---|---|
specifier | string | — | The module specifier to resolve. |
parentURL | URL | — | The WHATWG URL to resolve specifier relative to. |
opts? | ResolveOptions | — | Resolution options. |
Returns URL — The WHATWG URL that specifier resolves to.
Throws
UNEXPECTED_PROMISE— the protocol answered asynchronously.MODULE_NOT_FOUND— no module matchingspecifiercould be found relative toparentURL.TypeError—specifieris not a string,parentURLis not a WHATWGURL, orconditionis not a known condition.
path: string
Module.protocol: Protocol
The ModuleProtocol class for resolving, reading and loading modules. See Protocols for usage. From v7.2.1, the default protocol, and the protocol a graph reads through once it has been patched with loader.patch(), keep their identity across calls.
resolutions: ResolutionsMap
The resolutions map of the module graph, with keys being serialized parent URLs and values being "imports" maps.
url: URL
The WHATWG URL identifier of the module.
ModuleLoader
ModuleLoader.new ModuleLoader(opts?: ModuleLoader.Options)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
opts? | ModuleLoader.Options | — | — |
ModuleLoader.cache: Cache
The cache of loaded modules that the loader was created with, shared with every module of its graph.
ModuleLoader.evict(urls: URL | string | Iterable<URL | string>): URL[]
Forget what the graph knows about the modules at urls, so that they are read and resolved again when next asked for. urls is a WHATWG URL, a string, or any number of either. Returns the URLs that were known. Added in v7.2.0.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
urls | URL | string | Iterable<URL | string> | — | — |
ModuleLoader.get(url: URL): Module | null
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
ModuleLoader.import(entry: URL, opts?: ModuleLoader.ImportOptions): Promise<unknown>
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
entry | URL | — | — |
opts? | ModuleLoader.ImportOptions | — | — |
ModuleLoader.importSync(entry: URL, opts?: ModuleLoader.ImportOptions): unknown
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
entry | URL | — | — |
opts? | ModuleLoader.ImportOptions | — | — |
ModuleLoader.instantiate
instantiate(urls: URL | string | Iterable<URL | string>, opts?: ModuleLoader.LinkOptions): Promise<Module[]>Compile the modules at urls and everything they reach, without evaluating any of them, and return the module records. This is the phase link() performs on the way to an entry, for modules the graph already holds and is not entering through, so it answers whether a source can be run at all without any of the consequences of running it. Unlike link(), it does not set main. A module the graph has already compiled is left as it is, so ask after a patch() rather than before one. The result says whether the source compiles, not whether everything it names exists: an unresolved specifier is deferred. Added in v7.2.0.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
urls | URL | string | Iterable<URL | string> | — | — |
opts? | ModuleLoader.LinkOptions | — | — |
ModuleLoader.instantiateSync
instantiateSync(urls: URL | string | Iterable<URL | string>, opts?: ModuleLoader.LinkOptions): Module[]The synchronous equivalent of instantiate(). It drives the protocol's synchronous methods, which throw an UNEXPECTED_PROMISE error when the protocol can only answer asynchronously. Added in v7.2.0.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
urls | URL | string | Iterable<URL | string> | — | — |
opts? | ModuleLoader.LinkOptions | — | — |
ModuleLoader.link
link(entry: URL, source?: Buffer | string | null, opts?: ModuleLoader.LinkOptions): Promise<Module>Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
entry | URL | — | — |
source? | Buffer | string | null | — | — |
opts? | ModuleLoader.LinkOptions | — | — |
ModuleLoader.linkSync(entry: URL, source?: Buffer | string | null, opts?: ModuleLoader.LinkOptions): Module
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
entry | URL | — | — |
source? | Buffer | string | null | — | — |
opts? | ModuleLoader.LinkOptions | — | — |
ModuleLoader.main: Module
The module representing the entry script where the program was launched.
ModuleLoader.ImportOptions
interface ImportOptions {
attributes?: Attributes
mount?: URL | string
}The same options as ModuleLoader.LinkOptions.
ModuleLoader.LinkOptions
interface LinkOptions {
attributes?: Attributes
mount?: URL | string
}attributes are the import attributes, for example the { type: 'json' } in import foo from 'foo' with { type: 'json' }. mount is the URL to mount the entry at if it is a bundle, defaulting to the URL of the bundle followed by a /. The bundle serves only the modules under it, so file:/// lets it serve any file. Added in v7.3.0. See Loading bundles. instantiate() and instantiateSync() do not apply mount.
ModuleLoader.Options
interface Options {
builtins?: Builtins
cache?: Cache
concurrency?: number
defaultType?: number
imports?: ImportsMap
protocol?: Protocol
resolutions?: ResolutionsMap
wasi?: (url: URL) => object
}wasi is a function returning the WASI options of a WebAssembly addon, given its URL, for addons not imported with a wasi attribute. Without it, they get no capabilities except clocks. The options are versioned, so version must be 'preview1'. A cache is read with a single wasi, like a single protocol: reading it from a loader with another wasi throws CACHE_INCOMPATIBLE. Added in v7.5.0.
ModuleLoader.patch(bundle: Bundle): URL[]
Make the contents of bundle what the graph reads, in front of whatever it read before, and forget what it knew about each key. Returns the URLs the patch covers. A later patch of the same key wins over an earlier one. Forgetting a module is not re-reading it: nothing runs again until something asks for it, and whoever already imported it keeps what it exported. To reach a patched module through an importer the graph still holds, evict that importer too. Added in v7.2.0.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
bundle | Bundle | — | — |
ModuleLoader.protocol: Protocol
The ModuleProtocol modules are resolved and read through, shared with every module of the loader's graph.
ModuleProtocol
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
| Parameter | Type | Default | Description |
|---|---|---|---|
methods? | Partial<ModuleProtocol> | — | Protocol method overrides; any of resolve, resolveSync, exists, existsSync, read, readSync, list, or listSync. |
context? | ModuleProtocol | — | An existing protocol to fall back to for any method not provided in methods. |
ModuleProtocol.exists(url: URL): boolean | Promise<boolean>
Return whether the URL exists.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | The URL to check for existence. |
Returns boolean | Promise<boolean> — Whether url exists. The default implementation returns false.
ModuleProtocol.existsSync(url: URL): boolean
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
ModuleProtocol.extend(methods: Partial<ModuleProtocol>): ModuleProtocol
Create a new protocol that uses this protocol as its context, overriding the given methods.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
methods | Partial<ModuleProtocol> | — | Protocol method overrides for the new protocol. |
Returns ModuleProtocol — A new ModuleProtocol that uses this protocol as its context, with methods overriding.
ModuleProtocol.list(url: URL): Iterable<URL> | AsyncIterable<URL>
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
ModuleProtocol.listSync(url: URL): Iterable<URL>
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
ModuleProtocol.isProtocol(value: unknown): boolean
Return whether value is a protocol of the same version as this one, whichever copy of bare-module created it.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
value | unknown | — | The value to test. |
Returns boolean — Whether value is a protocol of the same version as this one, whichever copy of bare-module created it.
ModuleProtocol.read(url: URL): Buffer | string | null | Promise<Buffer | string | null>
Return the source code of a URL, represented as a string or buffer.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | The URL to read. |
Returns Buffer | string | null | Promise<Buffer | string | null> — The source of url as a Buffer or string, or null if it does not exist.
ModuleProtocol.readSync(url: URL): Buffer | string | null
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
ModuleProtocol.resolve(url: URL): URL | Promise<URL>
Resolve a module URL to the URL that should be linked. The default implementation returns url unchanged.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | The URL to resolve. |
Returns URL | Promise<URL> — The resolved URL. A promise must not be returned during synchronous linking.
ModuleProtocol.resolveSync(url: URL): URL
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
Types
Attributes
interface Attributes {
type?: Lowercase<keyof typeof constants>
imports?: string
host?: string
wasi?: string
}Import attributes instructing how a module should be loaded. A type that is not one of the known strings is turned down rather than passed over. host and wasi were added in v7.5.0:
| Attribute | Description |
|---|---|
host | The host to resolve the addons of the module and its dependencies for, such as wasi-wasm32. The conditions of the host may then also be targeted alongside those of the native host, in the order a package lists them. See WebAssembly addons. |
wasi | The specifier of a module providing the WASI capabilities of the WebAssembly addons of the module and its dependencies, resolved relative to the parent URL. |
Cache
interface Cache {
}A map of module URL hrefs to loaded modules.
LoadOptions
interface LoadOptions {
referrer?: Module
builtins?: Builtins
cache?: Cache
concurrency?: number
defaultType?: number
imports?: ImportsMap
protocol?: Protocol
resolutions?: ResolutionsMap
wasi?: (url: URL) => object
attributes?: Attributes
mount?: URL | string
}LoadOptions carries everything in ModuleLoader.Options and ModuleLoader.LinkOptions, so mount applies here as it does to the loader. Added in v7.3.0.
ResolveOptions
interface ResolveOptions {
referrer?: Module
builtins?: Builtins
cache?: Cache
concurrency?: number
defaultType?: number
imports?: ImportsMap
protocol?: Protocol
resolutions?: ResolutionsMap
wasi?: (url: URL) => object
}CreateRequireOptions
interface CreateRequireOptions {
referrer?: Module
builtins?: Builtins
cache?: Cache
concurrency?: number
defaultType?: number
imports?: ImportsMap
protocol?: Protocol
resolutions?: ResolutionsMap
wasi?: (url: URL) => object
}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, opts?: RequireOptions): string
(specifier: string, parentURL?: string | URL, opts?: RequireOptions): 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.
bare-module/protocol
ModuleProtocol
protocol.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
| Parameter | Type | Default | Description |
|---|---|---|---|
methods? | Partial<ModuleProtocol> | — | Protocol method overrides; any of resolve, resolveSync, exists, existsSync, read, readSync, list, or listSync. |
context? | ModuleProtocol | — | An existing protocol to fall back to for any method not provided in methods. |
protocol.ModuleProtocol.exists(url: URL): boolean | Promise<boolean>
Return whether the URL exists.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | The URL to check for existence. |
Returns boolean | Promise<boolean> — Whether url exists. The default implementation returns false.
protocol.ModuleProtocol.existsSync(url: URL): boolean
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
protocol.ModuleProtocol.extend(methods: Partial<ModuleProtocol>): ModuleProtocol
Create a new protocol that uses this protocol as its context, overriding the given methods.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
methods | Partial<ModuleProtocol> | — | Protocol method overrides for the new protocol. |
Returns ModuleProtocol — A new ModuleProtocol that uses this protocol as its context, with methods overriding.
protocol.ModuleProtocol.list(url: URL): Iterable<URL> | AsyncIterable<URL>
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
protocol.ModuleProtocol.listSync(url: URL): Iterable<URL>
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
protocol.ModuleProtocol.isProtocol(value: unknown): boolean
Return whether value is a protocol of the same version as this one, whichever copy of bare-module created it.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
value | unknown | — | The value to test. |
Returns boolean — Whether value is a protocol of the same version as this one, whichever copy of bare-module created it.
protocol.ModuleProtocol.read(url: URL): Buffer | string | null | Promise<Buffer | string | null>
Return the source code of a URL, represented as a string or buffer.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | The URL to read. |
Returns Buffer | string | null | Promise<Buffer | string | null> — The source of url as a Buffer or string, or null if it does not exist.
protocol.ModuleProtocol.readSync(url: URL): Buffer | string | null
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
protocol.ModuleProtocol.resolve(url: URL): URL | Promise<URL>
Resolve a module URL to the URL that should be linked. The default implementation returns url unchanged.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | The URL to resolve. |
Returns URL | Promise<URL> — The resolved URL. A promise must not be returned during synchronous linking.
protocol.ModuleProtocol.resolveSync(url: URL): URL
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
bare-module/loader
ModuleLoader
loader.ModuleLoader.new ModuleLoader(opts?: ModuleLoader.Options)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
opts? | ModuleLoader.Options | — | — |
loader.ModuleLoader.cache: Cache
The cache of loaded modules that the loader was created with, shared with every module of its graph.
loader.ModuleLoader.evict(urls: URL | string | Iterable<URL | string>): URL[]
Forget what the graph knows about the modules at urls, so that they are read and resolved again when next asked for. urls is a WHATWG URL, a string, or any number of either. Returns the URLs that were known. Added in v7.2.0.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
urls | URL | string | Iterable<URL | string> | — | — |
loader.ModuleLoader.get(url: URL): Module | null
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | URL | — | — |
loader.ModuleLoader.import(entry: URL, opts?: ModuleLoader.ImportOptions): Promise<unknown>
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
entry | URL | — | — |
opts? | ModuleLoader.ImportOptions | — | — |
loader.ModuleLoader.importSync(entry: URL, opts?: ModuleLoader.ImportOptions): unknown
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
entry | URL | — | — |
opts? | ModuleLoader.ImportOptions | — | — |
loader.ModuleLoader.instantiate
instantiate(urls: URL | string | Iterable<URL | string>, opts?: ModuleLoader.LinkOptions): Promise<Module[]>Compile the modules at urls and everything they reach, without evaluating any of them, and return the module records. This is the phase link() performs on the way to an entry, for modules the graph already holds and is not entering through, so it answers whether a source can be run at all without any of the consequences of running it. Unlike link(), it does not set main. A module the graph has already compiled is left as it is, so ask after a patch() rather than before one. The result says whether the source compiles, not whether everything it names exists: an unresolved specifier is deferred. Added in v7.2.0.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
urls | URL | string | Iterable<URL | string> | — | — |
opts? | ModuleLoader.LinkOptions | — | — |
loader.ModuleLoader.instantiateSync
instantiateSync(urls: URL | string | Iterable<URL | string>, opts?: ModuleLoader.LinkOptions): Module[]The synchronous equivalent of instantiate(). It drives the protocol's synchronous methods, which throw an UNEXPECTED_PROMISE error when the protocol can only answer asynchronously. Added in v7.2.0.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
urls | URL | string | Iterable<URL | string> | — | — |
opts? | ModuleLoader.LinkOptions | — | — |
loader.ModuleLoader.link
link(entry: URL, source?: Buffer | string | null, opts?: ModuleLoader.LinkOptions): Promise<Module>Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
entry | URL | — | — |
source? | Buffer | string | null | — | — |
opts? | ModuleLoader.LinkOptions | — | — |
loader.ModuleLoader.linkSync(entry: URL, source?: Buffer | string | null, opts?: ModuleLoader.LinkOptions): Module
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
entry | URL | — | — |
source? | Buffer | string | null | — | — |
opts? | ModuleLoader.LinkOptions | — | — |
loader.ModuleLoader.main: Module
The module representing the entry script where the program was launched.
loader.ModuleLoader.patch(bundle: Bundle): URL[]
Make the contents of bundle what the graph reads, in front of whatever it read before, and forget what it knew about each key. Returns the URLs the patch covers. A later patch of the same key wins over an earlier one. Forgetting a module is not re-reading it: nothing runs again until something asks for it, and whoever already imported it keeps what it exported. To reach a patched module through an importer the graph still holds, evict that importer too. Added in v7.2.0.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
bundle | Bundle | — | — |
loader.ModuleLoader.protocol: Protocol
The ModuleProtocol modules are resolved and read through, shared with every module of the loader's graph.
Types
Options
interface Options {
builtins?: Builtins
cache?: Cache
concurrency?: number
defaultType?: number
imports?: ImportsMap
protocol?: Protocol
resolutions?: ResolutionsMap
wasi?: (url: URL) => object
}wasi is a function returning the WASI options of a WebAssembly addon, given its URL, for addons not imported with a wasi attribute. Without it, they get no capabilities except clocks. The options are versioned, so version must be 'preview1'. A cache is read with a single wasi, like a single protocol: reading it from a loader with another wasi throws CACHE_INCOMPATIBLE. Added in v7.5.0.
LinkOptions
interface LinkOptions {
attributes?: Attributes
mount?: URL | string
}mount is the URL to mount the entry at if it is a bundle. Added in v7.3.0. See Loading bundles.
ImportOptions
interface ImportOptions {
attributes?: Attributes
mount?: URL | string
}CommonJS modules
require(specifier[, options])
Used to import JavaScript or JSON modules and local files. Relative paths such as ./, ./foo, ./bar/baz, and ../foo are resolved against the directory named by __dirname. POSIX-style paths are resolved in an OS-independent fashion, so the same relative paths work on Windows as they do on POSIX systems.
Returns the exported module contents.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
specifier | string | — | The module specifier to resolve and load. |
options? | RequireOptions | — | Options for the call; with.type selects how the file should be loaded — one of 'script', 'module', 'json', 'bundle', 'addon', 'binary', or 'text' (see Module.constants). A type that is not one of these is turned down rather than passed over, as is a type that is not a string. with.imports names an "imports" map to apply to the module and its dependencies, with.host selects the WebAssembly build of their addons, and with.wasi names its WASI provider (see WebAssembly addons). |
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.
require.resolve(specifier[, parentURL][, options])
Uses the internal machinery of require() to resolve specifier relative to the URL parentURL and returns the path string.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
specifier | string | — | The module specifier to resolve. |
parentURL? | string | URL | the module's own URL | The URL to resolve specifier relative to. When it is passed, the resolutions are recorded for parentURL rather than the calling module, which is useful when resolving on behalf of another module. |
options? | RequireOptions | — | Added in v7.2.0. If with is passed, the graph of the resolved module is linked, but not evaluated, with the attributes applied, so that its resolutions are recorded in module.resolutions. That lets the module be prepared later from its path alone, such as when spawning a thread with an imports map applied: require.resolve('./worker.js', { with: { imports: './imports.json' } }). |
require.addon([specifier][, parentURL])
Also used to import modules, but 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's exports:
module.exports = require.addon()require.addon.host
The platform and architecture used when resolving addons, following the pattern <platform>-<arch>[-<environment>]. The same value as Bare.Addon.host.
require.addon.resolve([specifier][, parentURL])
Resolves specifier relative to parentURL as an addon and returns the path string, using the addon resolution algorithm.
require.asset(specifier[, parentURL])
Resolves specifier relative to parentURL and returns the path of the asset as a string. Can be used to load assets, for example to load ./foo.txt from the local file system:
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 imports 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. For more on import syntax, see MDN.
The with clause takes the same attributes as the with option of require(), including host and wasi:
import { DatabaseSync } from 'bare-sqlite' with { host: 'wasi-wasm32', wasi: './wasi.js' }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.
import.meta.resolve(specifier[, parentURL][, options])
A module-relative resolution function that returns the URL string for the module. specifier is a string resolved relative to parentURL, a WHATWG URL. options are the same as for require.resolve(), and were added in v7.2.0.
import.meta.addon([specifier][, parentURL])
Also used to import modules, but loads only addon modules. specifier is resolved relative to parentURL using the addon resolution algorithm.
Returns the exported module contents.
import.meta.addon.host
The platform and architecture used when resolving addons, following the pattern <platform>-<arch>[-<environment>]. The same value as Bare.Addon.host.
import.meta.addon.resolve([specifier][, parentURL])
Resolves specifier relative to parentURL as an addon and returns the URL string, using the addon resolution algorithm.
import.meta.asset(specifier[, parentURL])
Resolves specifier relative to parentURL and returns the URL of the asset as a string.
WebAssembly addons
Added in v7.5.0, which ships with Bare 1.36.0. An addon package can ship a WebAssembly build, prebuilds/wasi-wasm32/<name>.wasm, next to its native prebuilds. The loader runs it through bare-wasm-abi, sandboxed by bare-wasi: through WASI, the addon reaches only what its WASI provider grants. It still reaches whatever JavaScript in its realm can, because the ABI hands it the global object.
Loading an addon as WebAssembly
The host attribute asks for the WebAssembly build of the addons of a module and everything it imports. The wasi attribute names the WASI provider for them:
const { DatabaseSync } = require('bare-sqlite', {
with: { host: 'wasi-wasm32', wasi: './wasi.js' }
})wasi-wasm32 is the only host the attribute accepts. A package with no .wasm build throws ADDON_NOT_FOUND. When the specifier is a string literal, write both attributes as string literals too: the import is linked before the module runs, using only the attributes written as literals, so a computed value conflicts with that link, and the call throws ADDON_HOST_INCOMPATIBLE or ADDON_WASI_INCOMPATIBLE. With a computed specifier, computed attributes work. With host set, the wasi and wasm32 conditions of a package's "exports" and "imports" match alongside those of the native host, in the order the package lists them. Without it, they are tried only as a fallback, after the native conditions.
Without a host attribute, the loader uses the native build of an addon when one exists, and falls back to the .wasm build when there is no native prebuild or linked addon for the host. A fallback addon gets the provider of a wasi attribute on its import, which works without host, or else the loader's wasi option, which Bare doesn't set. With neither, WASI gives it no capabilities except clocks.
WASI providers
A WASI provider is a module that exports a function, as module.exports or its default export. The loader calls it with the URL of each WebAssembly addon and passes what it returns to new WASI():
// wasi.js
const { DirectorySync } = require('bare-directory')
const { HostDirectory } = require('bare-wasi/host')
module.exports = (url) => ({
version: 'preview1',
preopens: {
'/': new HostDirectory(DirectorySync.open(__dirname + '/data'))
}
})The wasi specifier is resolved relative to the importing module and loaded like any other module. A provider that exports neither a function nor an object whose default is a function throws a TypeError when the addon is evaluated. A provider function that returns nothing, such as an arrow function with a block body, leaves the addon with the default options. The provider runs as application code, so install what it imports, such as bare-wasi and bare-directory: the copy of bare-wasi that Bare bundles for its loader can't be imported. Options without version: 'preview1' throw a WASIError with code INVALID_VERSION. An addon imported without a wasi attribute gets the options from the loader's wasi option, or only clocks through WASI.
The README of v7.5.0 shows a provider that grants randomness with crypto.getRandomValues(). Bare has no crypto global, so use randomFillSync() from bare-crypto instead.
One context per package
The addons of a package load in one context per loader: the host and WASI provider it is first imported under. Importing it under another throws:
| Error code | Thrown when |
|---|---|
ADDON_HOST_INCOMPATIBLE | A package's addons are already resolved for the default hosts and it is imported with host, or the other way round, or an addon below a host import is not WebAssembly. Also thrown, from require.addon() and catchable, as Cannot resolve addon … which is linked in several contexts, when one package's addons are reached both natively and with host, such as bare-directory imported with host while a provider loads it natively. |
ADDON_WASI_INCOMPATIBLE | A package's WebAssembly addons already have one WASI provider and it is imported with another. In v7.5.0, a dynamic import() with a wasi attribute also throws it, naming the same provider. |
UNKNOWN_ADDON_HOST | A host attribute is not wasi-wasm32. |
These are thrown by bare-module-traverse while the importing module is linked, before any of its code runs, so a try block around a static require() doesn't catch them. A require() whose specifier the lexer can't read, such as a computed name, is linked when it runs, so these errors are thrown from that call and can be caught, and so are those from a computed attribute next to a string-literal specifier. A require.resolve() of the package counts as importing it with the default hosts. An ADDON_NOT_FOUND for a missing .wasm build is thrown when the module that loads the addon runs, and can be caught around a require() or import(), but not around a static import declaration.
In v7.5.0, a dynamic import() doesn't apply its wasi attribute. With a string-literal specifier, it throws ADDON_WASI_INCOMPATIBLE, naming the same provider. With a computed specifier, the addon loads with the default options. Pass a provider with require() or a static import.
After Bare.Addon.seal()
Once the process is sealed, a native addon loads only if the process already loaded it, while WebAssembly addons still load. When resolving an addon on a sealed process, the loader skips native builds that Bare.Addon.loaded() reports as not loaded, so a package that also ships a .wasm build falls back to it, with the capabilities described above. This applies only to addons resolved after the seal. An addon reached through string-literal require() or import() specifiers from the module that calls Bare.Addon.seal(), or from any module linked with it before the seal, was resolved at link time, so its native addon throws CANNOT_LOAD even when the module that loads it runs after the seal. A package resolved after the seal that ships no .wasm build throws ADDON_NOT_FOUND, where Bare 1.35 threw CANNOT_LOAD. The provider's own native addons, such as bare-directory for a HostDirectory, follow the same rules, so load them before calling Bare.Addon.seal(), or the import fails. None of this applies inside a bundle: its modules keep the resolutions recorded at pack time, so after the seal a native addon the process hasn't loaded throws CANNOT_LOAD, even when the bundle also holds the .wasm build.
For a walkthrough, see Run an addon sandboxed in WebAssembly. For how the sandbox works and what it doesn't stop, see WebAssembly addons.
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:
| 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.
See also
- Builds on
bare-bundle,bare-module-lexer,bare-module-resolve,bare-module-traverse,bare-path,bare-type-stripper,bare-url, andbare-wasm-abi. bare-module-resolveandbare-module-traverse— the resolution and traversal algorithms underneath.- Bare runtime API —
Bare.Addon, the native-addon loaderrequire.addonbuilds on. - Bare modules — the full
bare-*catalog. - Bare runtime API — the runtime these modules extend.
Last updated on