blob: b85cf3c301d3e01bca8ed4da4f5b871fca41ca8f [file]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import 'package:logging/logging.dart';
import '../logging.dart';
import '../pubspec.dart';
import '../serve/dev_workflow.dart';
import 'configuration.dart';
import 'shared.dart';
/// Command to run a server for local web development with the build daemon.
class ServeCommand extends Command<int> {
@override
final name = 'serve';
@override
final description =
'Run a local web development server and a file system'
' watcher that rebuilds on changes.';
@override
final argParser = ArgParser(usageLineLength: lineLength)
..addOption(
autoOption,
help: '''
Automatically performs an action after each build:
restart: Reload modules and re-invoke main (loses current state)
refresh: Performs a full page refresh.
''',
allowed: ['restart', 'refresh'],
)
..addFlag(
debugFlag,
help:
'Enable the launching of DevTools (Alt + D / Option + D). '
'This also enables --$launchInChromeFlag.',
)
..addFlag(
debugExtensionFlag,
help:
'Enable the backend for the Dart Debug Extension. '
'Learn more at dart.dev/to/web-debug-extension.',
)
..addFlag(
enableInjectedClientFlag,
help:
'Whether or not to inject the client.js script in web apps. This '
'is required for all debugging related features, but may interact '
'poorly with proxy servers or other environments.',
defaultsTo: true,
)
..addSeparator('Advanced:')
..addOption(
chromeDebugPortFlag,
help:
'Specify which port the Chrome debugger is listening on. '
'If used with $launchInChromeFlag Chrome will be started with the'
' debugger listening on this port.',
)
..addOption(
hostnameFlag,
help: 'Specify the hostname to serve on.',
defaultsTo: 'localhost',
)
..addFlag(
hotRestartFlag,
negatable: false,
help:
'Automatically reloads changed modules after each build '
'and restarts your application.\n'
"Can't be used with $liveReloadFlag.",
hide: true,
)
..addFlag(hotReloadFlag, negatable: false, hide: true)
..addFlag(
launchInChromeFlag,
help:
'Automatically launches your application in Chrome with the '
'debug port open. Use $chromeDebugPortFlag to specify a specific '
'port to attach to an already running chrome instance instead.',
)
..addOption(
userDataDirFlag,
defaultsTo: null,
help:
'Use with $launchInChromeFlag to specify user data directory '
'to pass to chrome. Will start chrome window logged into default '
'profile with enabled extensions. Use `auto` as a value to infer '
'the default directory for the current OS. '
'Note: only supported for Mac OS X and linux platforms.',
)
..addFlag(
liveReloadFlag,
negatable: false,
help:
'Automatically refreshes the page after each successful build.\n'
"Can't be used with $hotRestartFlag.",
hide: true,
)
..addFlag(
logRequestsFlag,
negatable: false,
help: 'Enables logging for each request to the server.',
)
..addOption(
tlsCertChainFlag,
help:
'The file location to a TLS Certificate to create an HTTPs server.\n'
'Must be used with $tlsCertKeyFlag.',
)
..addOption(
tlsCertKeyFlag,
help:
'The file location to a TLS Key to create an HTTPs server.\n'
'Must be used with $tlsCertChainFlag.',
)
..addSeparator('Common:');
ServeCommand() {
addSharedArgs(argParser, releaseDefault: false);
}
@override
String get invocation => '${super.invocation} [<directory>[:<port>]]...';
@override
Future<int> run() async {
Configuration configuration;
configuration = Configuration.fromArgs(argResults);
configureLogWriter(configuration.verbose);
try {
await validatePubspecLock(configuration);
} on PackageException catch (e) {
logWriter(Level.SEVERE, 'Pubspec errors: ', error: '${e.details}');
rethrow;
}
// Forward remaining arguments as Build Options to the Daemon.
// This isn't documented. Should it be advertised?
final buildOptions = buildRunnerArgs(configuration)
..addAll(argResults!.rest.where((arg) => arg.startsWith('-')).toList());
final extraArgs = argResults?.rest ?? [];
final directoryArgs = extraArgs
.where((arg) => !arg.startsWith('-'))
.toList();
final targetPorts = parseDirectoryArgs(directoryArgs);
validateLaunchApps(configuration.launchApps, targetPorts.keys);
final workflow = await DevWorkflow.start(
configuration,
buildOptions,
targetPorts,
);
await workflow.done;
return 0;
}
}