A powerful Dart code generation library for seamlessly integrating Dart objects with MiniScript. This package provides decorators and automated code generation to make Dart classes accessible from MiniScript with minimal boilerplate.
- Decorator-based: Use simple
@visible
and@hide
annotations to control which properties and methods are exposed to MiniScript - Build runner integration: Automatic wrapper generation using
build_runner
for seamless development workflow - Automatic code generation: Generates wrapper classes that handle conversion between Dart and MiniScript types
- Type-safe conversions: Handles all common Dart types (String, int, double, bool, List, Map) automatically
- Caching system: Efficiently manages object instances to prevent duplicates and handle circular references
- Property access: Full support for getting and setting properties from MiniScript
- Method calls: Expose Dart methods as callable functions in MiniScript
- Memory management: Smart caching with cleanup capabilities
Add this to your pubspec.yaml
:
dependencies:
miniscript: ^1.6.2-6
source_gen: ^1.4.0
build: ^2.4.0
dev_dependencies:
build_runner: ^2.4.0
analyzer: ^6.0.0
- Annotate your classes with
@visible
and optionally@hide
for specific properties:
import 'package:miniscriptgen/miniscriptgen.dart';
part 'player.g.dart';
@visible
class Player {
String name;
int health;
int score;
List<String> inventory;
@hide
String secretData; // This won't be accessible from MiniScript
Player(this.name, this.health, this.score)
: inventory = [], secretData = 'hidden';
void takeDamage(int damage) {
health -= damage;
if (health < 0) health = 0;
}
void heal(int amount) {
health += amount;
if (health > 100) health = 100;
}
}
- Generate wrapper classes using build_runner:
# Generate wrappers automatically
dart run build_runner build
# Or use watch mode for continuous generation
dart run build_runner watch
- Use the generated wrappers in your code:
import 'package:miniscript/miniscript_interpreter.dart';
import 'package:miniscriptgen/miniscriptgen.dart';
// Import your model
import 'player.dart';
void main() {
// Register generated wrappers
registerPlayerWrapper();
// Create your objects
var player = Player('Hero', 100, 0);
// Create wrapper instance
var playerWrapper = createPlayerWrapper(player);
// Create MiniScript interpreter
var interpreter = Interpreter();
interpreter.standardOutput = (text, addLineBreak) => print('MiniScript: $text');
// Add wrapped objects to MiniScript global scope
interpreter.globals['player'] = playerWrapper;
// Run MiniScript code that uses your objects
interpreter.execute('''
print "Player: " + player.name
print "Health: " + player.health
print "Score: " + player.score
// Modify properties
player.health = player.health - 10
player.score = player.score + 50
print "After battle:"
print "Health: " + player.health
print "Score: " + player.score
''');
}
The MiniScript Generator includes full build_runner integration for automatic wrapper generation with comprehensive testing and cleanup.
- Configure build.yaml (optional, default configuration works for most cases):
targets:
$default:
builders:
miniscriptgen|miniscript_wrapper_generator:
enabled: true
generate_for:
- lib/**
- example/**
- test/**
options:
output_directory: "lib/generated"
generate_setup_function: true
- Add part declarations to files containing
@visible
classes:
part 'your_file.g.dart';
# One-time build
dart run build_runner build
# Watch mode (rebuilds on file changes)
dart run build_runner watch
# Clean generated files
dart run build_runner clean
# Build with conflict resolution
dart run build_runner build --delete-conflicting-outputs
Use the provided helper scripts for better control:
# Build with enhanced output
dart scripts/build.dart build
# Watch mode with better logging
dart scripts/build.dart watch
# Complete cleanup (removes ALL generated files)
dart scripts/build.dart clean
# Clean and rebuild
dart scripts/build.dart rebuild
For each @visible
class, the generator creates:
- Wrapper Class:
YourClassWrapper extends BaseWrapper<YourClass>
- Factory Function:
YourClassWrapper createYourClassWrapper(YourClass object)
- Registration Function:
void registerYourClassWrapper()
/// Generated wrapper for Player
class PlayerWrapper extends BaseWrapper<Player> {
PlayerWrapper(Player dartObject) : super(dartObject);
@override
List<String> getPropertyNames() {
return ["name", "health", "score", "inventory", "takeDamage", "heal"];
}
@override
Value? getProperty(String propertyName) {
switch (propertyName) {
case "name":
return dartToMiniScript(dartValue.name);
case "health":
return dartToMiniScript(dartValue.health);
case "takeDamage":
return _createTakeDamageMethod();
// ... other properties
}
}
@override
bool setProperty(String propertyName, Value? value) {
switch (propertyName) {
case "name":
var dartValue = miniScriptToDart(value);
if (dartValue is String || dartValue == null) {
this.dartValue.name = dartValue;
return true;
}
return false;
// ... other setters
}
}
// Method wrappers...
}
@visible
: Mark a class to be accessible from MiniScript. All public properties and methods will be exposed unless marked with@hide
.@hide
: Mark specific properties or methods to be hidden from MiniScript, even if the class is@visible
.
The base class for all generated wrappers. It extends ValMap
and provides:
- Property access through
getProperty()
andsetProperty()
- Automatic type conversion between Dart and MiniScript values
- Integration with the MiniScript engine through
evalOverride
andassignOverride
A singleton cache system that:
- Manages wrapper factory registrations
- Handles object-to-wrapper conversions
- Prevents duplicate object wrapping
- Provides global
dartToMiniScript()
andminiScriptToDart()
functions
The SimpleMiniScriptGenerator
creates:
- Wrapper classes that extend
BaseWrapper
- Property getter/setter implementations
- Method binding code
- Registration functions for the cache
The system automatically handles conversion between Dart and MiniScript types:
Dart Type | MiniScript Type | Notes |
---|---|---|
String |
ValString |
Direct conversion |
int |
ValNumber |
Converted to double |
double |
ValNumber |
Direct conversion |
bool |
ValNumber |
true = 1.0, false = 0.0 |
List<Value?> |
ValList |
Recursive conversion of elements |
Map<Value?, Value?> |
ValMap |
Recursive conversion of values |
null |
ValNull |
Null values |
Custom Objects | BaseWrapper<T> |
Uses registered wrappers |
Methods are exposed as callable objects in MiniScript:
// In MiniScript
player.takeDamage(25) // Calls the Dart method
player.heal(10) // Calls the Dart method
Properties can be read and written from MiniScript:
// In MiniScript
name = player.name // Get property
player.name = "NewName" // Set property
health = player.health // Get property
player.health = 50 // Set property
The system gracefully handles:
- Type conversion errors
- Property access to non-existent properties
- Method calls with wrong parameters
- Null values and edge cases
The cache system provides:
- Automatic cleanup of unused objects
- Prevention of memory leaks
- Statistics and monitoring
- Manual cache clearing when needed
The MiniScript Generator includes a comprehensive test suite to ensure reliability and proper functionality.
dart run build_runner build --ignore-conflicting-outputs
dart test
The test suite includes:
- Basic Wrapper Tests: Property access, method calls, type validation
- Build Runner Tests: Code generation, file management, build process
- Cleanup Tests: File cleanup, environment reset, dependency tracking
- Type Conversion Tests: Dart ↔ MiniScript type conversion validation
- Error Handling Tests: Edge cases, invalid inputs, error recovery
- Performance Tests: Wrapper creation speed, memory usage, cache efficiency
test/
├── comprehensive_test.dart # Main test suite
├── conversion_test.dart # Type conversion tests
├── generated_wrapper_test.dart # Generated wrapper tests
├── models.dart # Models for tests
├── test_model.dart # Basic test model
├── wrapper_demo.dart # Wrapper demonstration
example/
├── simple_test.dart # Basic usage examples
├── demo.dart # Demo
├── debug_test.dart # Debug utilities
└── models/ # Test model classes
See the example/
directory for comprehensive examples:
models/
: Sample classes with@visible
annotationsdemo.dart
: Complete demonstration of generated wrappersexample_usage.dart
: Usage examples and integration patternssimple_test.dart
: Basic testing examples- Build runner integration examples with real generated code
// Convert Dart object to MiniScript value
Value? dartToMiniScript(dynamic dartValue)
// Convert MiniScript value to Dart object
dynamic miniScriptToDart(Value? value)
// Setup cache with all wrappers
void setupMiniScriptCache()
// Register a wrapper factory
void registerWrapper<T>(WrapperFactory<T> factory)
// Check if wrapper exists
bool hasWrapper<T>()
// Get cache statistics
Map<String, dynamic> getCacheStats()
// Clear cache
void clearCache()
// Create class descriptor
ClassDescriptor createClassDescriptor(String name, {
List<PropertyDescriptor> properties = const [],
List<PropertyDescriptor> methods = const [],
})
// Generate wrapper code
String generateWrapperForClassDescriptor(ClassDescriptor classDesc)
- Use annotations consistently: Always mark classes with
@visible
and use@hide
for sensitive data - Use build_runner: Leverage automatic code generation instead of manual wrapper creation
- Handle type conversions: Be aware of type conversion rules, especially for numbers and booleans
- Manage cache lifecycle: Clear the cache periodically in long-running applications
- Test thoroughly: Use the comprehensive test suite to verify functionality
- Error handling: Implement proper error handling for MiniScript execution
- Watch mode: Use
dart run build_runner watch
during development for automatic rebuilds - Clean environment: Use
dart scripts/build.dart clean
for complete cleanup - Validate setup: Run
dart scripts/test.dart validate
before major changes - Monitor performance: Use the performance tests to track wrapper efficiency
- Method calls with complex parameter types need manual implementation
- No support for generic types in wrapper generation
- Build runner integration requires part files
- Generated code depends on the analyzer package version
- Test environment requires proper cleanup between runs
- Performance testing may vary based on system resources
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality using the test framework
- Run
dart run build_runner build && dart test
to validate changes - Ensure all tests pass with
dart test
- Use
dart run build_runner clean
before committing - Submit a pull request with test results
This project is licensed under the MIT License - see the LICENSE file for details.