Dart CLI Application Best Practices
- When to use this skill
Use this skill when:
-
Creating a new Dart CLI application.
-
Refactoring an existing CLI entrypoint (bin/ ).
-
Reviewing CLI code for quality and standards.
-
Setting up executable scripts for Linux/Mac.
- Best Practices
Entrypoint Structure (bin/ )
Keep the contents of your entrypoint file (e.g., bin/my_app.dart ) minimal. This improves testability by decoupling logic from the process runner.
DO:
// bin/my_app.dart import 'package:my_app/src/entry_point.dart';
Future<void> main(List<String> arguments) async { await runApp(arguments); }
DON'T:
-
Put complex logic directly in bin/my_app.dart .
-
Define classes or heavy functions in the entrypoint.
Executable Scripts
For CLI tools intended to be run directly on Linux and Mac, add a shebang and ensure the file is executable.
DO:
-
Add #!/usr/bin/env dart to the first line.
-
Run chmod +x bin/my_script.dart to make it executable.
#!/usr/bin/env dart
void main() => print('Ready to run!');
Process Termination (exitCode )
Properly handle process termination to allow for debugging and correct status reporting.
DO:
-
Use the exitCode setter to report failure.
-
Allow main to complete naturally.
-
Use standard exit codes (sysexits) for clarity (e.g., 64 for bad usage, 78 for configuration errors).
-
See package:io ExitCode class or FreeBSD sysexits man page.
import 'dart:io';
void main() { if (someFailure) { exitCode = 64; // DO! return; } }
AVOID:
- Calling exit(code) directly, as it terminates the VM immediately, preventing "pause on exit" debugging and finally blocks from running.
Exception Handling
Uncaught exceptions automatically set a non-zero exit code, but you should handle expected errors gracefully.
Example:
Future<void> main(List<String> arguments) async { try { await runApp(arguments); } catch (e, stack) { print('App crashed!'); print(e); print(stack); exitCode = 1; // Explicitly fail } }
- Recommended Packages
Use these community-standard packages owned by the Dart team to solve common CLI problems:
Category Recommended Package Usage
Stack Traces package:stack_trace
detailed, cleaner stack traces
Arg Parsing package:args
standard flag/option parsing
Testing package:test_process
integration testing for CLI apps
Testing package:test_descriptor
file system fixtures for tests
Networking package:http
standard HTTP client (remember user-agent!)
ANSI Output package:io
handling ANSI colors and styles
- Interesting community packages
Category Recommended Package Usage
Configuration package:json_serializable
strongly typed config objects
CLI Generation package:build_cli
generate arg parsers from classes
Version Info package:build_version
automatic version injection
Configuration package:checked_yaml
precise YAML parsing with line numbers
- Conventions
-
File Caching: Write cached files to .dart_tool/[pkg_name]/ .
-
User-Agent: Always set a User-Agent header in HTTP requests, including version info.