Turns an import specifier into a module the bundler can read.
What this replaces
There was no module resolution. rewriteRuntimeImports matched the runtime
entry and deleted everything else, so import { format } from 'date-fns' in
a component became nothing at all — a green build and a ReferenceError the
first time the action ran. Resolution is the difference between a build that
knows what an application depends on and one that guesses.
The rules, in order
- Virtual modules. The compiler owns component, page and entry generation, and hands those in as sources rather than files. They resolve by id and win over anything on disk.
- Relative and absolute paths. Probed for an exact file, then for the
Avenx and JavaScript extensions, then for a directory index. A specifier
that names a
.component.jsor.page.jsresolves to the compiled virtual module, never to the raw template file — the raw file is not JavaScript and would not parse. - Node builtins. Rejected, by name, with the reason. A browser bundle
that quietly contains
fsis a bundle that fails at load; saying so at build time is the whole point. - Bare specifiers. The Node algorithm, walking
node_modulesupward from the importing file, honouringexports,browser,moduleandmainin that order of preference for a browser target.
Nothing here falls back to "skip it". A specifier that cannot be resolved is a ResolveError, and the build fails with the importer and the specifier — because the alternative is what this file exists to end.
- Source:
Classes
Members
(static, constant) AVENX_PACKAGE_ROOT :string
The root of the installed avenx-core package, derived from this file.
Type:
- string
- Source:
(inner, constant) ASSET_EXTENSIONS :Set:.<string:>
Extensions an Avenx bundle has no way to represent as a module.
The old pipeline deleted these imports silently, so import './theme.css'
in main.app.js looked like it did something and did nothing at all. Saying
so is strictly better than either silence or a resolution error that reads
as a missing file.
Type:
- Source:
(inner, constant) EXTENSIONS :Array:.<string:>
Extensions probed for a path specifier that names no file directly.
Type:
- Source:
(inner, constant) NODE_BUILTINS :Set:.<string:>
Node builtin modules, with and without the node: prefix.
Listed rather than probed, because the answer must not depend on which Node
version is running the build: an application importing fs is broken in a
browser on every version, and the diagnostic should say so identically.
Type:
- Source:
(inner, constant) RUNTIME_SPECIFIER :RegExp
Specifiers that mean "the Avenx browser runtime".
The compiler has always accepted all three spellings, and a deep path into
lib/core besides, so resolution accepts exactly what the old rewriter did.
Type:
- RegExp
- Source:
(inner, constant) realPaths :Map:.<string:, string:>
Real paths, keyed by the path that was walked to reach them.
fs.realpathSync is a syscall per segment, and a large graph probes the same
package directory thousands of times. The cache is module-scoped rather than
per-Resolver because the answer is a property of the filesystem, not of a
build.
Type:
- Source:
Methods
(static) isRuntimeSpecifier(specifier) → {boolean}
Whether a specifier names the Avenx runtime entry.
Parameters:
| Name | Type | Description |
|---|---|---|
specifier |
string | The import specifier. |
- Source:
Returns:
True for avenx-core, avenx-core/runtime or avenx-core/core.
- Type
- boolean
(static) probeFile(candidate) → {string|null}
Probes a path for a real file, trying Avenx and JavaScript extensions and a directory index.
Every return goes through realPath, so the id the graph keys a module by is the file's own path rather than whichever route reached it.
Parameters:
| Name | Type | Description |
|---|---|---|
candidate |
string | An absolute path with or without an extension. |
- Source:
Returns:
The file that exists, or null.
- Type
- string | null
(static) realPath(target) → {string}
Canonicalises a path so that one file has one identity.
A module's identity in the graph is the path that resolved to it. Without
this, a file reachable by two paths becomes two modules -- and that is not a
corner case: npm link, a file: dependency, a pnpm store and every
workspace layout put a symlink between an application and its dependencies.
Duplicated source is the cheap half of the damage. The expensive half is
module state. lib/core/renderer/stringRenderer.js holds the registry the
fallback renderer installs itself into; emitted twice, the install fills one
copy and AvenxComponent reads the other, so a component whose template the
IR refused throws at its first render.
Node's own ESM loader canonicalises this way unless --preserve-symlinks is
passed, and Rollup, webpack and esbuild all do the same. Avenx now agrees
with them.
Falls back to the path as given when it cannot be resolved: a path that does not exist is not this function's error to raise, and the caller is already about to say so with a specifier and an importer.
Parameters:
| Name | Type | Description |
|---|---|---|
target |
string | An absolute path. |
- Source:
Returns:
The canonical path, or target when it cannot be read.
- Type
- string
(inner) applyBrowserField(manifest, packageDir, file) → {string|false}
Applies a package's browser field remapping to a resolved file.
The string form replaces the entry point. The object form maps individual
paths, and a false value means "this module is empty in a browser", which
is how packages ship Node-only branches — honouring it is what keeps a
fs shim from reaching the bundle.
Parameters:
| Name | Type | Description |
|---|---|---|
manifest |
object | The parsed manifest. |
packageDir |
string | The package root. |
file |
string | The file resolution produced. |
- Source:
Returns:
The remapped file, or false when it is stubbed out.
- Type
- string | false
(inner) readManifest(file, cache) → {object|null}
Reads and caches a package.json.
Parameters:
| Name | Type | Description |
|---|---|---|
file |
string | Absolute path to the manifest. |
cache |
Map:.<string:, (object:|null:)> | Shared manifest cache. |
- Source:
Returns:
The parsed manifest, or null when absent or invalid.
- Type
- object | null
(inner) resolveExports(manifest, subpath) → {string|null}
Resolves a subpath against a manifest's exports field.
Parameters:
| Name | Type | Description |
|---|---|---|
manifest |
object | The parsed package manifest. |
subpath |
string |
|
- Source:
Returns:
A relative target, or null when exports does not cover it.
- Type
- string | null
(inner) selectCondition(value) → {string|null}
Picks a target out of an exports value for a browser build.
Conditions are tried in the order a browser bundler should prefer them:
browser before import before module before default. require is
accepted last, because a package that offers only CommonJS is still better
bundled than reported missing — module:lib/bundler/interop decides
what to do with the format once the file is read.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
any | An |
- Source:
Returns:
A relative target, or null when nothing applies.
- Type
- string | null