| name | dart-cli-app-best-practices |
| description | Best practices for creating high-quality, executable Dart CLI applications.
Covers entrypoint structure, exit code handling, and recommended packages. |
| license | Apache-2.0 |
Dart CLI Application Best Practices
1. 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.
2. 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
}
}
Cross-Platform Compatibility (Windows Support)
When writing CLI applications and tests, ensure compatibility with Windows:
- Paths: Never hardcode path separators like
/. Use package:path's
p.join or p.normalize to construct paths portably.
- File Permissions: When testing file permission errors, remember that
chmod is not available on Windows. Use icacls on Windows or appropriate
mock libraries. Never skip tests on Windows simply because of permission
commands if a Windows equivalent exists.
3. Recommended Packages
Use these community-standard packages owned by the Dart team
to solve common CLI problems:
4. Interesting community packages
5. 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.