Module: lib/compiler/modules

Wraps what the compiler generates into real ES modules.

Where the boundary is

The compiler owns Avenx semantics: templates, declarations, expressions, scoped CSS, Atlas, the shape of a generated component class. The bundler owns modules: resolution, the dependency graph, dead-code elimination, emission.

This file is the seam. It takes the class declaration ComponentParser produces — unchanged, still a bare class X extends AvenxComponent — and gives it the module framing a graph can read: an import of the runtime, the developer's own imports preserved verbatim, and a default export.

Keeping the parser's output shape untouched is deliberate. It is what avenx-core/testing, the Vite plugin and six test files consume, and there is no reason a change to how modules are linked should change how a component is compiled.

Why the developer's imports are copied verbatim

This is the fix for the defect that motivated the whole migration. The old pipeline ran every module through rewriteRuntimeImports, which turned the runtime import into destructuring and deleted every other import. A component importing an npm package compiled to a green build and a ReferenceError in the browser, because the import simply ceased to exist.

Here an import statement is passed through untouched. If it names something that does not exist, the bundler says so and the build fails. There is no code path that removes one.

Source:

Methods

(static) bridgeModule(options) → {string}

Builds the ES module for a bridge.

A bridge file is already valid JavaScript, so almost nothing happens to it: its default export is named so the runtime can be told what to call it, and that is all. Its own imports — the runtime, other bridges — stay as they were written and become real edges in the graph, which is what replaced the old alias-and-concatenate scheme.

Parameters:
Name Type Description
options object

Module options.

Properties
Name Type Description
name string

The bridge's declared name.

binding string

The stable binding name for the bridge.

source string

The bridge source, after env substitution.

Source:
Returns:

The module source.

Type
string

(static) builtinComponentModule(specifier, tag) → {string}

A module that registers a built-in component.

Added to the entry graph only when a template in the build references the tag. <VirtualList> drags the template renderer and the DOM patcher with it, which is roughly 60 KB an application that never writes the tag has no reason to carry.

Parameters:
Name Type Description
specifier string

The published entry point that registers it.

tag string

The tag name, for the comment.

Source:
Returns:

The module source.

Type
string

(static) collectImportStatements(source) → {Array:.<string:>}

Extracts the import declarations from a source file, in order.

Used to carry a component's own imports into its generated module. The scanner-based module reader is not used here because this runs on Avenx template source, which is not JavaScript: an import line is the one JavaScript-shaped construct allowed at the top of a .component.js, and it is read as such.

Parameters:
Name Type Description
source string

The component or page source.

Source:
Returns:

The import statements, as written.

Type
Array:.<string:>

(static) componentModule(options) → {string}

Builds the ES module for a compiled component or page.

Parameters:
Name Type Description
options object

Module options.

Properties
Name Type Description
className string

The generated class name.

body string

The class declaration ComponentParser produced.

isPage boolean

Whether the unit is a page.

imports Array:.<string:>

The developer's own import statements.

bridgeBindings Array:.<{local:: string:, binding:: string:}>

Bridges the unit imported, so the class body's binding names resolve.

Source:
Returns:

The module source.

Type
string

(static) devtoolsModule(namespace) → {string}

Builds the development-only module that exposes the trace recorder.

Why this module exists at all

The recorder used to ship to every bundle, production included, because a pre-bundled runtime blob had no way to leave anything out. Now it ships when something references it, and in a production build nothing does -- which makes the documentation's promise that recording "never reaches a production build" structurally true rather than nearly true.

avenx serve --trace still has to work, and it works by calling window.Avenx.installTraceRecorder(...) from an injected script. So a development build imports the recorder here, which both keeps it in the graph and puts it where the dev server looks for it. Development and production differ in exactly this: whether the debugging tool is present.

Parameters:
Name Type Description
namespace string

The namespace global's name.

Source:
Returns:

The module source.

Type
string

(static) entryModule(options) → {string}

Builds the application entry module.

main.app.js is the developer's file and stays theirs. The compiler adds what it discovered for them — the pages under src/pages/, the bridges something imports — as ordinary imports and registrations, in the place the old pipeline injected them, so a project that worked before works now.

Parameters:
Name Type Description
options object

Entry options.

Properties
Name Type Attributes Description
source string

main.app.js, after env substitution.

registrations Array:.<{name:: string:, file:: string:, kind:: string:}>

Units to import and register.

prelude Array:.<string:> <optional>

Ids of modules to import before anything else: the documented globals, the development tools, the Rewind configuration.

Source:
Returns:

The module source.

Type
string

(static) globalsModule(names, namespace) → {string}

Builds the module that installs Avenx's documented globals.

A deliberate narrowing

The concatenated build had no module system, so generated component classes reached the runtime through bare globals and globalThis.Avenx had to carry the whole export surface for anything else to be reachable at all.

Generated modules now import what they use, so the global object is no longer a mechanism — it is a compatibility surface. It carries exactly the names lib/core/globals.js declares public. Importing the whole namespace to publish it would pin every module in the runtime into every bundle, which would trade a real saving for an escape hatch that imports already provide.

Parameters:
Name Type Description
names Array:.<string:>

The public global names.

namespace string

The namespace global's name.

Source:
Returns:

The module source.

Type
string

(static) interpreterModule() → {string}

Builds the module that installs the expression interpreter.

The counterpart of the recorder above, and present for the same reason: a development build should keep working while a template is being edited, and a production build should not carry a JavaScript parser that can never run.

Source:
Returns:

The module source.

Type
string

(static) pathSpecifier(target) → {string}

Turns an absolute path into a specifier a generated module can carry.

Absolute POSIX-style paths, because the resolver treats them as paths rather than package names and Windows separators would read as escape sequences inside the generated string literal.

Parameters:
Name Type Description
target string

Absolute path to the module.

Source:
Returns:

A specifier safe to embed.

Type
string

(static) rewindConfigModule(settings) → {string}

Builds the module that configures Rewind for this project.

A project that leaves rewind alone gets no module at all, so the defaults baked into the journal are the only thing shipped and the bundle is unchanged from what it was before Rewind existed.

Parameters:
Name Type Description
settings object

Non-default journal settings.

Source:
Returns:

The module source.

Type
string

(static) stringRendererModule() → {string}

A module that pulls the string renderer into the bundle.

Added to the entry graph only when at least one component in the build could not be compiled to a render program. An application whose every template compiles never references the renderer, so the bundler drops it -- about 87 KB of source that most applications no longer execute a line of.

Source:
Returns:

The module source.

Type
string