// tool/dart_dev/config.dart
import 'package:dart_dev/dart_dev.dart';
import 'package:io/io.dart';
int hello([DevToolExecutionContext context]) {
print('Hello!');
return ExitCode.success.code;
}
final config = {
'hello': DevTool.fromFunction(hello),
};$ ddev hello
Hello!// tool/dart_dev/config.dart
import 'package:args/args.dart';
import 'package:dart_dev/dart_dev.dart';
import 'package:io/io.dart';
final helloArgParser = ArgParser()
..addOption('name', help: 'Your name.');
int hello([DevToolExecutionContext context]) {
var name;
if (context?.argResults != null) {
name = context.argResults['name'];
}
print('Hello${name != null ? ', $name' : ''}!');
return ExitCode.success.code;
}
final config = {
'hello': DevTool.fromFunction(hello, argParser: helloArgParser),
};$ ddev hello --name Dart
Hello, Dart!// tool/dart_dev/config.dart
import 'package:dart_dev/dart_dev.dart';
final config = {
'github': DevTool.fromProcess(
'open', ['https://github.com/Workiva/dart_dev']),
};If you want to dynamically build a subprocess or run it conditionally based on command-line args, you can compose the function and process tools like so:
// tool/dart_dev/config.dart
import 'dart:async';
import 'package:args/args.dart';
import 'package:dart_dev/dart_dev.dart';
import 'package:io/io.dart';
final githubArgParser = ArgParser()..addFlag('open');
FutureOr<int> github([DevToolExecutionContext context]) async {
final url = 'https://github.com/Workiva/dart_dev';
final shouldOpen = context?.argResults != null && context.argResults['open'];
if (shouldOpen) {
return DevTool.fromProcess('open', [url]).run();
}
print(url);
return ExitCode.success.code;
}
final config = {
'github': DevTool.fromFunction(github, argParser: githubArgParser),
};$ ddev github
https://github.com/Workiva/dart_dev
$ ddev github --open
# opens https://github.com/Workiva/dart_dev in browserCompoundTool is designed to make it easy to compose multiple tools into a
single tool that can be run via a single ddev target.
Additionally, each tool added to a CompoundTool can be configured with one of
two possible run conditions:
- When passing: run only when all previous tools have succeeded (default)
- Always: run regardless of the success/failure of previous tools
Configuring a tool to always run is useful for set-up and tear-down tools, like starting and stopping a test server before and after running tests, respectively.
// tool/dart_dev/config.dart
import 'package:dart_dev/dart_dev.dart';
import 'package:io/io.dart';
int startServer([DevToolExecutionContext context]) {
// Start server and wait for it to be ready...
return ExitCode.success.code;
}
int stopServer([DevToolExecutionContext context]) {
// Stop server...
return ExitCode.success.code;
}
final config = {
'test': CompoundTool()
..addTool(DevTool.fromFunction(startServer), alwaysRun: true)
..addTool(TestTool())
..addTool(DevTool.fromFunction(stopServer), alwaysRun: true),
};The BackgroundProcessTool can be used in conjunction with CompoundTool to
wrap a tool with the starting and stopping of a background subprocess:
final testServer = BackgroundProcessTool(
'node', ['tool/server.js'],
delayAfterStart: Duration(seconds: 1));
final config = {
'test': CompoundTool()
..addTool(testServer.starter, alwaysRun: true)
..addTool(TestTool())
..addTool(testServer.stopper, alwaysRun: true),
};CompoundTool.addTool() supports an optional argMapper parameter that can be
used to customize the ArgResults instance that the tool gets when it runs.
The typedef for this argMapper function is:
typedef ArgMapper = ArgResults Function(ArgParser parser, ArgResults results);By default, subtools added to a CompoundTool will only receive option args
that are defined by their respective ArgParser:
// tool/dart_dev/config.dart
import 'package:dart_dev/dart_dev.dart';
final config = {
'example': CompoundTool()
// This subtool has an ArgParser that only supports the --foo flag.
..addTool(DevTool.fromFunction((_) => 0,
argParser: ArgParser()..addFlag('foo')))
// This subtool has an ArgParser that only supports the --bar flag.
..addTool(DevTool.fromFunction((_) => 0,
argParser: ArgParser()..addFlag('bar')))
};With the above configuration, running ddev example --foo --bar will result in
the compound tool running the first subtool with only the --foo option
followed by the second subtool with only the --bar option. Any positional args
would be discarded.
You may want one of the subtools to also receive the positional args. To
illustrate this, our test tool example from above can be updated to allow
positional args to be sent to the TestTool portion so that individual test
files can be targeted.
To do this, we can use the takeAllArgs function provided by dart_dev:
// tool/dart_dev/config.dart
import 'package:dart_dev/dart_dev.dart';
final config = {
'test': CompoundTool()
..addTool(DevTool.fromFunction(startServer), alwaysRun: true)
// Using `takeAllArgs` on this subtool will allow it to receive
// the positional args passed to `ddev test` as well as any
// option args specific to the `TestTool`.
..addTool(TestTool(), argMapper: takeAllArgs)
..addTool(DevTool.fromFunction(stopServer), alwaysRun: true),
};
int startServer([DevToolExecutionContext context]) => 0;
int stopServer([DevToolExecutionContext context]) => 0;The default behavior for subtools along with using takeAllArgs for the subtool
that needs the positional args should cover most use cases. However, you may
write your own ArgMapper function if further customization is needed.
With more complex use cases, it may be necessary to share or use state across the individual tools that make up a compound tool. To accomplish this, you can either create a closure or a class within which to share said state.
// tool/dart_dev/config.dart
import 'package:args/args.dart';
import 'package:dart_dev/dart_dev.dart';
import 'package:io/io.dart';
class NewAnalyzeTool extends AnalyzeTool with CompoundToolMixin {
NewAnalyzeTool() {
addTool(
DevTool.fromFunction(
_parseStrictMode,
argParser: _strictModeArgParser,
),
);
addTool(
DevTool.fromFunction(
_runAnalyzeTool,
argParser: AnalyzeTool().argParser,
),
);
}
bool _strictModeEnabled;
final _strictModeArgParser = ArgParser()..addFlag('strict');
int _parseStrictMode([DevToolExecutionContext context]) {
_strictModeEnabled = context?.argResults != null &&
context.argResults['strict'] ?? false;
return ExitCode.success.code;
}
Future<int> _runAnalyzeTool([DevToolExecutionContext context]) {
// Build an AnalyzeTool instance using this "NewAnalyzeTool"
// instance as the base config.
final analyzeTool = AnalyzeTool()
..analyzerArgs = analyzerArgs
..include = include;
// Augment the configuration if strict mode is enabled.
if (_strictModeEnabled) {
(analyzeTool.analyzerArgs ??= []).addAll([
'--fatal-lints',
'--fatal-infos',
'--fatal-warnings',
]);
}
return analyzeTool.run(context);
}
}
// Consume and configure the NewAnalyzeTool as if it were an instance of
// the AnalyzeTool, when in reality it is a compound tool.
final config = {
'analyze': NewAnalyzeTool()
..analyzerArgs = ['--no-implicit-dynamic'],
};