macro_kit 0.5.0
macro_kit: ^0.5.0 copied to clipboard
A development-time macro system for Dart that generates code instantly for fast iteration without build_runner.
Macro Kit #
A blazingly fast code generation tool for Dart that generates code instantly on save—no build runner required.
✨ Features #
- ⚡ Lightning Fast: Code generation in under 100ms after initial run
- 💾 Instant Generation: Press Ctrl+S and watch your code appear immediately
- 🐛 Easy Debugging: Debug your macros or third-party packages in real-time to understand and fix generation issues
- 🚫 No Build Runner: Eliminate slow build processes and generated file conflicts
- 🎯 Flexible & Capable: Handles most day-to-day code generation needs with macros
- 🔄 Live Development: Changes take effect instantly—no separate build step needed
📦 Installation #
1. Activate the macro tool globally #
dart pub global activate macro_kit
Or install from source:
dart pub global activate --source path ./
if you updating, just deactivate first and activate again.
2. Add macro_kit to your project #
# pubspec.yaml
dependencies:
macro_kit: ^latest_version
3. Configure the plugin #
# analysis_options.yaml
plugins:
macro_kit: ^latest_version
4. Create macro context file #
Create a file named macro_context.dart in your lib directory.
If you have the macro_kit plugin installed:
- Create an empty
macro_context.dartfile inlib/ - The plugin will automatically populate it with setup code and inline documentation
Manual setup (without plugin):
import 'dart:async';
import 'package:macro_kit/macro_kit.dart';
bool get autoRunMacro => true;
List<String> get autoRunMacroCommand => macroDartRunnerCommand;
/// Entry point for automatic macro execution - do not modify
void main() async {
await setupMacro();
await keepMacroRunner();
}
/// Configure and register your macros here
Future<void> setupMacro() async {
await runMacro(
package: PackageInfo('your_package_name'), // TODO: Update this
autoRunMacro: autoRunMacro,
enabled: true,
macros: {
'DataClassMacro': DataClassMacro.initialize,
// Add more macros here
},
);
}
How it works:
- When
autoRunMacro = true(default): The macro system runs this file in a separate background process, generating code automatically without running your app - When
autoRunMacro = false(debug mode): You must callsetupMacro()from your app'smain()to trigger generation inside your running application (see step 5)
Customization:
- Change
autoRunMacroCommandto customize how the macro process runs (add flags, use different runners, etc.) - Register additional macros in the
macrosmap
Important
Platform support:
- Desktop (macOS, Windows, Linux): Full support for both automatic and manual modes
- Mobile & Web: Automatic mode only - manual macro execution is not supported and will be ignored
5. Optional: Initialize macros in your app #
Import macro_context.dart into your main.dart and call setupMacro():
import 'package:my_package/macro_context.dart' as macro;
void main() async {
await macro.setupMacro();
runApp(MyApp());
}
When is this needed?
| Scenario | Behavior |
|---|---|
autoRunMacro = true (default) |
Optional - Only listens for messages sent by the macro server without generating code; doesn't generate code (desktop only, no-op on mobile/web) |
autoRunMacro = false (debug mode) |
Required - Triggers code generation inside your app (desktop only, ignored on mobile/web) |
| Production builds | No effect - Automatically disabled in release mode |
Tip
Debugging macros: Set autoRunMacro = false in macro_context.dart, then call setupMacro()
from your app's main(). This lets you use breakpoints and inspect macro execution in real-time.
🚀 Quick Start #
1. Annotate your class #
import 'package:macro_kit/macro_kit.dart';
@dataClassMacro
class User with UserData {
const User({
required this.id,
required this.name,
required this.email,
});
final int id;
final String name;
final String email;
}
Note
Dart source code must be placed inside the lib directory for the macro to generate code
properly. However, for testing purposes, you can pass an absolute path instead of a package name
to
force it to load into the analysis context.
2. Save and generate #
Press Ctrl+S to save. Generation happens instantly!
- First run: ~3-5 seconds (one-time setup)
- Subsequent runs: <100ms ⚡
3. Use the generated code #
The macro automatically generates:
- ✅
fromJson(Map<String, dynamic> json)constructor - ✅
toJson()method - ✅ Equality operators (
==,hashCode) - ✅
copyWith()method - ✅
toString()method
// Use it immediately
final User user = UserData.fromJson({'id': 1, 'name': 'Alice', 'email': 'alice@example.com'});
final json = user.toJson();
final updated = user.copyWith(name: 'Bob');
4. Debug when needed #
Unlike build_runner, you can debug macro code generation in real-time. Run your app in debug mode and step through the generation process to identify and fix issues.
📚 Built-in Macros #
DataClassMacro #
Generates data class boilerplate including fromJson, toJson, copyWith, equality operators, and
toString.
@dataClassMacro
class UserProfile with UserProfileData {
const UserProfile({required this.name, required this.age});
final String name;
final int age;
}
For more information, see the Data Class Macro documentation.
AssetPathMacro #
Generates type-safe constants for your asset paths. Never hardcode asset strings again!
Future<void> setupMacro() async {
await runMacro(
macros: {
'DataClassMacro': DataClassMacro.initialize,
'AssetPathMacro': AssetPathMacro.initialize,
// Add your own macros or import from packages
},
assetMacros: {
'assets': [
AssetMacroInfo(
macroName: 'AssetPathMacro',
extension: '*',
output: 'lib',
),
],
},
);
}
For more information, see the Asset Path Macro documentation.
// Usage in code
final asset = Image.asset(AssetPaths.logo);
final asset = Image.asset(AssetPaths.icons.home);
🎯 Why Macro Kit? #
| Feature | Macro Kit | build_runner |
|---|---|---|
| Generation Speed | <100ms | Seconds to minutes |
| Debugging | ✅ Full debug support | ❌ Limited |
| File Conflicts | ❌ Never | ✅ Common issue |
| IDE Integration | ✅ Instant feedback | ⏳ Wait for build |
| Learning Curve | 🟢 Simple | 🔴 Complex |
Run the macro #
From CLI:
dart run lib/macro_context.dart
From IDE:
Simply open lib/macro_context.dart and run it directly.
Auto-rebuild configuration #
Configuration is defined in the macro.json file. We recommend using CLI-based generation
primarily for CI/CD pipelines and automated testing. During regular development, the IDE plugin
automatically loads context and regenerates code when you save files—no manual code generation
needed, just like writing regular Dart code.
For CI/CD and testing environments, you'll need to set up manual generation:
- Install macro_kit:
dart pub global activate macro_kit - Start the macro server in a separate process:
macro(normally handled by the plugin). Alternatively, you can import internal functions likestartMacroServerdirectly if you prefer not to activate the plugin globally (CI only) - Add absolute paths for directories to regenerate—context is loaded dynamically without requiring the analyzer plugin
To wait for regeneration to complete, call waitUntilRebuildCompleted after runMacro.
Enable auto-rebuild in macro.json:
{
"config": {
"auto_rebuild_on_connect": true,
"always_rebuild_on_connect": false
}
}
auto_rebuild_on_connect: Automatically rebuilds when the macro server connectsalways_rebuild_on_connect: Rebuilds on every reconnection (useful for CI environments)
⚠️ Current Limitations #
Macros can currently only be applied to classes. This covers most common use cases, but future updates will include:
- 🔜 Support for applying macros to variables and functions
- 🔜 Additional macro capabilities for library developers
- 🔜 More built-in macros for common patterns
Despite these limitations, Macro Kit handles the majority of day-to-day code generation needs efficiently.
🤝 Contributing #
Contributions are welcome! Feel free to:
- 🐛 Report bugs and issues
- 💡 Suggest new features
- 🔧 Submit pull requests
📄 License #
MIT License - see LICENSE for details