Compile-Time Superpowers
MetaScript macros generate code at compile-time, not runtime. Zero overhead, full type safety, and you can inspect what they produce.
See What Macros Generate
Unlike runtime reflection, MetaScript macros run during compilation. The generated code is type-checked and optimized just like hand-written code.
You write:
@derive(Eq, Clone, Debug)
class Point {
x: number
y: number
}Compiler generates:
class Point {
x: number
y: number
equals(other: Point): boolean {
return this.x === other.x
&& this.y === other.y
}
clone(): Point {
return new Point(this.x, this.y)
}
toString(): string {
return `Point { x: ${this.x}, y: ${this.y} }`
}
}Run msc expand src/file.ms anytime to see exactly what code macros generate. No magic, no hidden behavior.
Built-in Macros
@derive(...traits)
Auto-generate common methods based on class fields. The most-used macro.
Traits: Eq, Hash, Clone, Debug, Serialize, Default
@comptime { ... }
Execute code at compile-time. Results are embedded as constants in the output.
Capabilities: Arbitrary expressions, File reads, API calls
@serialize(options)
Customize serialization with field mapping, renaming, and format options.
Options: rename_all, skip_null, flatten
@test
Mark a function as a test case. Discovered and run by msc test.
Options: name, timeout, skip
@comptime: Compile-Time Execution
Execute arbitrary code during compilation. Read files, fetch configs, compute constants—all at build time, not runtime.
// This runs at compile-time:
@comptime {
const config = JSON.parse(
fs.readFileSync("./config.json")
)
}
// 'config' is now a compile-time constant
// Embedded directly in the binary
console.log(config.apiUrl) // No file read at runtime!Writing Custom Macros
Macros are just MetaScript functions that transform AST nodes. They're defined in std/macros/ and you can write your own.
// Define a custom macro
@macro
function logger(target: ClassDecl): ClassDecl {
// Add logging to every method
for (const method of target.methods) {
method.body = wrapWithLogging(method.body)
}
return target
}
// Use it
@logger
class UserService {
async getUser(id: string) { ... }
}