Module: lib/bundler/emit

Renders a linked module graph into one classic script.

The output shape, and why

One outer IIFE. Inside it, each module is an inner IIFE assigned to a var, emitted in dependency order:

(function () {
  'use strict';
  var __avx3 = (function () { var __x = {}; ...body...; return __x; })();
  var __avx7 = (function () { var { thing } = __avx3; ...body...; })();
})();

var rather than const is load-bearing: var hoists to the top of the outer function, so a module emitted before its dependency — which happens exactly once per cycle — can still name it. The binding is undefined at that moment and holds the exports object by the time anything dereferences it, which is the same shape the language itself has across a cycle.

Three problems this file has to get right

Live bindings. reactive/watcher.js has export let activeWatcher, it reassigns it, and runtime/AvenxComponent.js reads it across the module boundary to decide whether it is inside its own render. Copying the value at import time would capture null forever and break that check silently. So a mutable exported binding is hoisted out of its module into bundle scope under its own name: the exporter's assignments and every importer's reads then resolve to one variable through the ordinary scope chain, with no identifier rewriting anywhere. Exports are exposed as getters for the same reason, so a namespace or a re-export barrel sees the current value.

Cycles. Across the one edge that closes a cycle, the dependency's exports object does not exist yet. ES modules make exactly one guarantee there — function declarations hoist — so this emitter reproduces exactly that: a cyclic import of a function becomes a forwarder that dereferences on call, and a cyclic import of anything else is a build error, because in a browser it would be a temporal dead zone.

Line fidelity. Every rewritten declaration is emitted on a single line and padded back to the line count it replaced, so a module's body keeps a 1:1 line correspondence with its source. That is what makes the source map exact and a production stack trace point at a line the developer wrote.

Source:

Classes

EmitError

Members

(inner, constant) PRELUDE :string

The prelude every bundle carries.

One helper, five lines. A module system that needs more machinery than this at runtime has moved work out of the build that belonged in it.

Type:
  • string
Source:

Methods

(static) emitBundle(options) → {Object}

Renders a linked graph into a single classic script.

Parameters:
Name Type Description
options object

Emission options.

Properties
Name Type Attributes Description
graph object

The module graph.

order Array:.<string:>

Module ids in emission order.

included Set:.<string:> <optional>

Modules that survived tree shaking.

banner string <optional>

Text placed before the outer IIFE.

footer string <optional>

Text placed inside the outer IIFE, after every module, where it can read __avx_entry.

rootDir string <optional>

Root for source-map paths.

sourceMap boolean <optional>

Whether to build a source map.

file string <optional>

Output file name, for the map.

Source:
Throws:

When the graph cannot be rendered correctly.

Type
EmitError
Returns:

The bundle.

Type
Object

(static) mutableExports(module) → {Set:.<string:>}

Finds the exported bindings a module can reassign.

Only let and var declarations qualify. A const, a function and a class binding cannot be reassigned, so copying their value at import time is indistinguishable from a live binding and costs nothing.

Parameters:
Name Type Description
module object

A graph module.

Source:
Returns:

Names that must live at bundle scope.

Type
Set:.<string:>

(inner) buildSourceMap(options) → {object}

Builds a line-level source map for an emitted bundle.

Line-level rather than column-level, and exact rather than approximate: every rewritten declaration was padded back to the line count it replaced, so output line N of a module's body is source line N of that module. A stack trace from a production bundle therefore names a file and a line the developer wrote.

Parameters:
Name Type Description
options object

Map options.

Properties
Name Type Description
graph object

The module graph.

segments Array:.<object:>

Where each module's body landed.

rootDir string

Root for relative source paths.

file string

The generated file name.

Source:
Returns:

A source map, version 3.

Type
object

(inner) cyclicBinding(local, imported, target, importer, slotFor) → {string}

Builds a binding for an import that crosses a cycle.

The dependency's exports object does not exist yet at this point — the importing module is running inside the call that will produce it — so the forwarder cannot go through it. It goes through a bundle-scope slot instead, which the exporting module fills from its hoisted function declaration before its own body runs. That is exactly the order ES modules use, so a cyclic call that works in a browser works here and one that does not, does not.

Parameters:
Name Type Description
local string

The local name to declare.

imported string

The name being imported.

target object

The exporting graph module.

importer object

The importing graph module.

slotFor function

Allocates the shared slot.

Source:
Throws:

When the binding cannot legally cross a cycle.

Type
EmitError
Returns:

The declaration.

Type
string

(inner) functionExportLocal(module, exported) → {string|null}

The local name under which a module declares an exported function.

This is the question that decides whether a cyclic import is legal, so it is answered from the declaration rather than guessed from usage: only a function declaration is initialised before any module body runs, which is the one guarantee ES modules make across a cycle.

Parameters:
Name Type Description
module object

The exporting graph module.

exported string

The exported name.

Source:
Returns:

The local declaration name, or null when the export is not a function declaration.

Type
string | null

(inner) lineCount(text) → {number}

Counts the lines a chunk of text occupies.

Parameters:
Name Type Description
text string

The text.

Source:
Returns:

Line count, minimum one.

Type
number

(inner) moduleBinding(index) → {string}

A valid JavaScript identifier for a module's exports object.

Parameters:
Name Type Description
index number

The module's index in emission order.

Source:
Returns:

The binding name.

Type
string

(inner) padToLines(generated, original) → {string}

Pads generated text so it occupies the same number of lines it replaced.

Parameters:
Name Type Description
generated string

Replacement text, expected to be one line.

original string

The text being replaced.

Source:
Returns:

The replacement, newline-padded.

Type
string

(inner) renderCjsBody(context) → {string}

Wraps a CommonJS module so the bundle can evaluate it.

Parameters:
Name Type Description
context object

Emission context.

Properties
Name Type Description
module object

The graph module.

indexOf Map:.<string:, number:>

Module id to emission index.

included Set:.<string:>

Module ids in the bundle.

Source:
Returns:

The wrapped body.

Type
string

(inner) renderEsmBody(context) → {string}

Renders one module's body with its declarations rewritten.

Parameters:
Name Type Description
context object

Emission context.

Properties
Name Type Description
module object

The graph module.

graph object

The module graph.

indexOf Map:.<string:, number:>

Module id to emission index.

hoisted Set:.<string:>

Names living at bundle scope.

included Set:.<string:>

Module ids that reached the bundle.

slotFor function

Allocates a bundle-scope slot for a function crossing a cycle.

Source:
Throws:

When a construct cannot be rendered correctly.

Type
EmitError
Returns:

The transformed module body.

Type
string