Skip to main content
Ant implements a full ES module system with support for both ECMAScript modules (ESM) and CommonJS. The runtime includes built-in Node.js-compatible modules and Ant-specific modules with the ant: prefix.

ES modules (ESM)

Ant fully supports modern ES module syntax:
// Named exports
export const greeting = 'Hello';
export function sayHello(name) {
  return `${greeting}, ${name}!`;
}

// Default export
export default class MyClass {
  constructor(value) {
    this.value = value;
  }
}

// Re-exports
export { something } from './other-module.js';
export * from './utils.js';

Importing modules

// Named imports
import { greeting, sayHello } from './module.js';

// Default import
import MyClass from './module.js';

// Namespace import
import * as utils from './utils.js';

// Side-effect import
import './polyfills.js';

// Dynamic import
const module = await import('./dynamic.js');

Module resolution

Ant’s module loader resolves modules using Node.js-compatible algorithm:
// From esm/loader.c - resolution logic
static char *esm_resolve_node_module(
  const char *specifier, 
  const char *base_path
);

Resolution order

  1. Built-in modules: ant: prefix and Node.js core modules
  2. Relative paths: ./, ../
  3. Absolute paths: /path/to/module
  4. node_modules: Package resolution
  5. URL imports: https:// remote modules

File extensions

Ant tries these extensions in order:
// From esm/loader.c
const char *module_resolve_extensions[] = {
  ".js",
  ".mjs",
  ".cjs",
  ".json",
  ".ts",   // TypeScript (auto-stripped)
  ".tsx",
  NULL
};
TypeScript files are automatically transpiled at runtime with type annotations stripped.

Built-in modules with ant: prefix

Ant provides several built-in modules with the ant: prefix:

ant:shell

Execute shell commands:
import { $ } from 'ant:shell';

// Execute command and get result
const result = $`ls -la`;
console.log(result.text());

// Command with interpolation
const dir = '/tmp';
const output = $`ls ${dir}`;

// Example from examples/demo/destr.js
import { $ } from 'ant:shell';
import parse from 'https://esm.sh/destr';

const data = $`maid -g json-hydrated`.text();
console.log(parse(data));

ant:ffi

Foreign Function Interface for calling native libraries:
import { dlopen, suffix, FFIType } from 'ant:ffi';

// Load native library
const lib = dlopen(`./libmath.${suffix}`, {
  add: {
    args: [FFIType.i32, FFIType.i32],
    returns: FFIType.i32
  },
  multiply: {
    args: [FFIType.double, FFIType.double],
    returns: FFIType.double
  }
});

// Call native functions
const sum = lib.symbols.add(5, 3);        // 8
const product = lib.symbols.multiply(2.5, 4.0);  // 10.0

ant:lmdb

Lightning Memory-Mapped Database:
import { open } from 'ant:lmdb';

// Open database
const db = open('./mydb', { create: true });

// Store and retrieve data
db.put('key', 'value');
const value = db.get('key');

// Transactions
db.transaction(() => {
  db.put('key1', 'value1');
  db.put('key2', 'value2');
});

ant:os

Operating system utilities:
import { uptime, loadavg, constants } from 'ant:os';

console.log('Uptime:', uptime());
console.log('Load average:', loadavg());
console.log('Signal constants:', constants.signals);

ant:path

Path manipulation utilities:
import { join, resolve, dirname, basename } from 'ant:path';

const fullPath = join('/usr', 'local', 'bin');  // '/usr/local/bin'
const absolute = resolve('./relative/path');     // absolute path
const dir = dirname('/path/to/file.js');        // '/path/to'
const file = basename('/path/to/file.js');      // 'file.js'

Node.js compatible modules

Ant implements many Node.js core modules:

fs and fs/promises

import { readFile, writeFile, mkdir } from 'fs/promises';

// Read file
const content = await readFile('file.txt', 'utf-8');

// Write file
await writeFile('output.txt', 'Hello, World!');

// Create directory
await mkdir('new-dir', { recursive: true });

buffer

import { Buffer } from 'buffer';

// Create buffer
const buf = Buffer.from('Hello');

// Convert to string
const str = buf.toString('utf-8');

// Base64 encoding
const base64 = buf.toString('base64');

crypto

import { randomBytes, createHash } from 'crypto';

// Generate random bytes
const random = randomBytes(32);

// Hash data
const hash = createHash('sha256')
  .update('Hello, World!')
  .digest('hex');

events

import { EventEmitter } from 'events';

class MyEmitter extends EventEmitter {}

const emitter = new MyEmitter();

emitter.on('event', (data) => {
  console.log('Event received:', data);
});

emitter.emit('event', { message: 'Hello' });

util

import { promisify, inspect } from 'util';

// Promisify callback-based function
const readFileAsync = promisify(fs.readFile);
const content = await readFileAsync('file.txt', 'utf-8');

// Inspect objects
console.log(inspect({ a: 1, b: { c: 2 } }, { depth: null, colors: true }));

Remote module imports

Ant supports importing modules directly from URLs:
// Import from CDN
import parse from 'https://esm.sh/destr';

// Use imported module
const data = parse('{"key": "value"}');
Remote modules are:
  • Fetched over HTTPS
  • Cached for subsequent imports
  • Validated for security

CommonJS interop

Ant supports CommonJS modules for compatibility:
// CommonJS export
module.exports = {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b
};

// CommonJS require
const math = require('./math.cjs');
console.log(math.add(2, 3));

ESM ↔ CJS

You can import CommonJS from ESM:
// Import CommonJS module in ESM
import math from './math.cjs';
// or
import { add } from './math.cjs';

Module caching

Modules are cached after first load:
// From esm/loader.c
typedef struct esm_module {
  char *path;
  char *resolved_path;
  jsval_t namespace_obj;
  jsval_t default_export;
  bool is_loaded;
  bool is_loading;
  // ...
} esm_module_t;

static esm_module_cache_t global_module_cache = {NULL, 0};
Cache behavior:
  • First import loads and executes the module
  • Subsequent imports return cached exports
  • Dynamic import() can bypass cache
  • Module code runs only once

import.meta

Access module metadata:
// Current module URL
console.log(import.meta.url);

// Is this the main module?
if (import.meta.main) {
  console.log('Running as main module');
}

// Module directory (Ant-specific)
console.log(__dirname);
console.log(__filename);

Dynamic imports

Load modules dynamically at runtime:
// Conditional import
if (condition) {
  const module = await import('./feature.js');
  module.doSomething();
}

// Dynamic path
const moduleName = 'utils';
const { helper } = await import(`./${moduleName}.js`);

// Lazy loading
button.addEventListener('click', async () => {
  const { heavyFunction } = await import('./heavy.js');
  heavyFunction();
});

Module initialization

Modules are initialized in dependency order:
// From esm/loader.c - module evaluation
jsval_t js_esm_eval_module_source(
  ant_t *js,
  const char *path,
  const char *source,
  size_t length,
  jsval_t namespace_obj
);
Initialization order:
  1. Parse module and discover dependencies
  2. Load and parse dependencies recursively
  3. Execute modules in dependency order
  4. Cache exports

TypeScript support

Ant automatically strips TypeScript type annotations:
// math.ts - TypeScript with types
export function add(a: number, b: number): number {
  return a + b;
}

export interface Point {
  x: number;
  y: number;
}

// Types are stripped at runtime
// No compilation step needed!
Ant strips type annotations but doesn’t type-check. Use tsc --noEmit for type checking during development.

Package.json support

Ant respects package.json fields:
{
  "type": "module",
  "main": "./index.js",
  "exports": {
    ".": "./index.js",
    "./utils": "./utils/index.js"
  }
}

Best practices

ES modules are the modern standard and provide better static analysis:
// ✅ Good: ES modules
import { func } from './module.js';
export { func };

// ❌ Avoid: CommonJS (unless needed for compatibility)
const { func } = require('./module');
module.exports = { func };
Always include .js or .mjs extensions in import paths:
// ✅ Good: includes extension
import { func } from './module.js';

// ❌ Avoid: missing extension (works but slower)
import { func } from './module';
Use index.js to aggregate exports:
// utils/index.js
export { add, subtract } from './math.js';
export { format } from './string.js';
export { fetch } from './http.js';

// Usage
import { add, format, fetch } from './utils/index.js';

Next steps

Runtime architecture

Learn about the runtime internals

Async/await

Master asynchronous programming

Build docs developers (and LLMs) love