dart2esm 0.1.1
dart2esm: ^0.1.1 copied to clipboard
A Dart-to-native-ESM compiler that emits clean modern JavaScript modules.
dart2esm #
dart2esm is a Dart-to-native-ESM compiler.
The compiler uses Dart's Common Front End and Kernel IR as the frontend, then emits clean ECMAScript modules. JavaScript and Node primitives are used directly where they are semantically equivalent, with small Dart semantic helpers emitted only when required.
The 0.1 release targets broad Dart syntax and SDK lowering to native ESM. The compiler targets modern JavaScript directly; compatibility profiles are intentionally out of scope.
Installation #
dart2esm 0.1.x requires Dart 3.12 because it reads that SDK's Kernel binary format directly.
dart pub global activate dart2esm
Usage #
dart2esm bin/main.dart -o dist/main.mjs
For a complete first run:
dart2esm example/hello.dart -o /tmp/hello.mjs
node /tmp/hello.mjs
By default the generated module invokes main() at top level. Use
--no-run-main when the output should be imported as a module without executing
the Dart entrypoint:
dart2esm lib/example.dart -o dist/example.mjs --no-run-main
Pass compile-time environment declarations with -Dkey=value or
--define=key=value; multiple values can be repeated or comma-separated.
Use --platform=node, --platform=browser, or --platform=neutral to select
host-specific SDK behavior. The default auto mode uses VM Kernel when
possible and falls back to the browser frontend for web-only libraries.
Use --metrics to print raw/gzip size, line count, and emitted helper count for
the generated ESM module. Add --compare-dart2js to also compile the same Dart
source with dart compile js -O2 and print dart2esm/dart2js size ratios:
dart2esm lib/example.dart -o dist/example.mjs --metrics --compare-dart2js
You can also compile an existing Kernel component:
dart2esm build/input.dill -o dist/input.mjs
Architecture #
- Dart source is lowered to Kernel with
dart compile kernel --no-link-platform, falling back to the SDKfrontend_serverwith the DDC platform for JS/web-only SDK libraries. - Kernel bytes are read with
package:kernel. - Reachability and semantic passes build the declarations and runtime symbols needed by the entry module.
- Kernel nodes are lowered to a structured ESM AST, normalized, and linked against runtime helpers at symbol granularity. Unreferenced helper functions are not emitted.
- Code generation prints native
.mjs. Public entry-library declarations are exported as ESM bindings. Imported local Dart libraries are bundled into the same output module and only re-exported when the Dart entry library exports them.
Platform Limits #
dart:io lowering is split by host capability:
- Pure value APIs such as
FileMode,HeaderValue,ContentType, andCookieemit ordinary ECMAScript and remain available in neutral output. - Client HTTP uses the Fetch and Headers Web APIs. Client WebSockets use the WebSocket Web API. These mappings work in browser builds and modern Node runtimes without importing Node modules.
- Files, directories, links, processes, and standard IO require
--platform=nodeor the defaultautomode. They emit staticnode:imports and are rejected by browser and neutral builds.
Fetch behavior intentionally follows the host Web API, including CORS,
credential, forbidden-header, and redirect rules. WebSocket custom handshake
headers, custom HttpClient instances, and Dart ping intervals have no Web API
equivalent and fail explicitly. Raw sockets, HTTP servers, custom TLS contexts,
and synchronous compression are not rewritten to superficially similar browser
APIs.
VM Compatibility Boundary #
Native ESM can cover Dart language semantics and SDK APIs that have an equivalent JavaScript, Web API, or Node primitive. It cannot be a byte-for-byte replacement for the Dart VM runtime:
Isolate.spawnandIsolate.runuse real Node Workers or browser module Workers for top-level entry functions. Ports useMessageChannel, and supported messages cross the worker boundary using structured cloning. Closures, custom object prototypes, paused startup, and VM-specific error/exit port behavior are rejected until the compiler can emit dedicated worker entry modules and reconstruction metadata.dart:ffiis rejected. A DartPointerrefers to native C memory andDynamicLibraryresolves C symbols; wrapping an integer address in a JavaScript object does not implement either behavior. Future FFI support requires an explicit Node Native Addon ABI or WebAssembly ABI backend.- VM service APIs, native extensions, and full runtime reflection have no general ESM equivalent and remain explicit compatibility boundaries.