import and export. Arc’s module system is based on the ES specification and uses a two-phase compilation model for optimal performance.
Module basics
Any.js or .mjs file is treated as an ES module in Arc. Modules have their own scope and must explicitly export values to make them available to other modules.
Creating a module
Create a filemath.js:
math.js
Importing from a module
Create a filemain.js:
main.js
File extensions are required in import specifiers. Write
'./math.js', not './math'.Export syntax
Arc supports all standard ES module export patterns:Named exports
Export multiple values by name:Default exports
Export a single default value:logger.js
Re-exports
Re-export values from other modules:utils.js
Export syntax reference
Export syntax reference
Import syntax
Arc supports all standard ES module import patterns:Named imports
Import specific exports:Default imports
Import the default export:Namespace imports
Import all exports as a namespace object:Mixed imports
Combine default and named imports:Side-effect imports
Import a module for its side effects only:Import syntax reference
Import syntax reference
Module resolution
Arc resolves module specifiers following these rules:Builtin modules
The
arc module is built-in and available everywhere:Currently,
arc is the only builtin module. The Arc global provides the same API without requiring an import.The compile_bundle workflow
Arc uses a two-phase module system for efficiency:Phase 1: Compile-time (AOT)
When you runarc main.js, Arc compiles all modules ahead-of-time:
The result is a pure data structure (the bundle) with no source code or AST — just bytecode.
Phase 2: Runtime evaluation
After compilation, Arc evaluates the bundle:Why two phases?
Why two phases?
The two-phase design enables:
- Serialization:
ModuleBundleis a pure Erlang term, serializable withterm_to_binary - Caching: Bundles can be cached and reused without re-parsing
- Deployment: Compile once, deploy bytecode without source files
- Performance: No parsing or compilation during runtime
Circular dependencies
Arc handles circular module dependencies correctly:a.js
b.js
a.jsstarts evaluating- Encounters
importfromb.js, starts evaluatingb.js b.jsencounters circularimportfroma.js- Since
a.jsis already evaluating,aisundefinedat this point b.jsfinishes, exportsb = 'B'a.jscontinues, now seesb = 'B'
Module scope
Each module has its own scope:Top-level this
In modules, top-levelthis is undefined (not globalThis):
Module vs script mode
Arc runs.js and .mjs files as modules, and .cjs files as scripts:
| Feature | Module mode | Script mode |
|---|---|---|
import/export | ✅ Yes | ❌ No |
Top-level await | ✅ Yes | ❌ No |
Top-level this | undefined | globalThis |
| Global scope | No | Yes |
| File extension | .js, .mjs | .cjs |
Error handling
Module errors fall into several categories:Unlike some bundlers, Arc does not validate that named imports exist at link time. Missing imports resolve to
undefined, matching browser ESM behavior.Example module structure
Here’s a complete example showing how to structure a multi-file Arc program:main.js
actors/counter.js
Limitations
Arc’s module system has some current limitations:- No dynamic import:
import()expressions are not yet supported - No package manager: No npm, no package.json, no node_modules
- No CDN imports: Can’t import from URLs like Deno
- No import maps: No custom resolution rules
- No conditional exports: No “browser” vs “node” exports
What’s next?
Actor programming
Combine modules with actors
Running code
Different ways to execute Arc programs