Chromium Code Reviews| Index: sdk/lib/_internal/pub/lib/src/command/serve.dart |
| diff --git a/sdk/lib/_internal/pub/lib/src/command/serve.dart b/sdk/lib/_internal/pub/lib/src/command/serve.dart |
| index a03bdd9904d967b2992395de588b146178c9a413..f7d91cb9635233321deccb7c867b9929bfa64108 100644 |
| --- a/sdk/lib/_internal/pub/lib/src/command/serve.dart |
| +++ b/sdk/lib/_internal/pub/lib/src/command/serve.dart |
| @@ -5,8 +5,10 @@ |
| library pub.command.serve; |
| import 'dart:async'; |
| +import 'dart:math' as math; |
| import 'package:barback/barback.dart'; |
| +import 'package:path/path.dart' as p; |
| import '../barback/build_environment.dart'; |
| import '../barback/pub_package_provider.dart'; |
| @@ -20,8 +22,12 @@ final _arrow = getSpecial('\u2192', '=>'); |
| /// Handles the `serve` pub command. |
| class ServeCommand extends PubCommand { |
| - String get description => "Run a local web development server."; |
| - String get usage => "pub serve"; |
| + String get description => |
| + 'Run a local web development server.\n\n' |
| + 'By default, this serves "web/" and "test/", but an explicit list of \n' |
| + 'directories to serve can be provided as well.'; |
| + String get usage => "pub serve [directories...]"; |
| + final takesArguments = true; |
| PubPackageProvider _provider; |
| @@ -35,7 +41,7 @@ class ServeCommand extends PubCommand { |
| ServeCommand() { |
| commandParser.addOption('port', defaultsTo: '8080', |
| - help: 'The port to listen on.'); |
| + help: 'The base port to listen on.'); |
| // A hidden option for the tests to work around a bug in some of the OS X |
| // bots where "localhost" very rarely resolves to the IPv4 loopback address |
| @@ -66,24 +72,26 @@ class ServeCommand extends PubCommand { |
| WatcherType.POLLING : WatcherType.AUTO; |
| return BuildEnvironment.create(entrypoint, hostname, port, mode, |
| - watcherType, ["web"].toSet(), |
| + watcherType, _directoriesToServe, |
| useDart2JS: useDart2JS).then((environment) { |
| // In release mode, strip out .dart files since all relevant ones have |
| // been compiled to JavaScript already. |
| if (mode == BarbackMode.RELEASE) { |
| - environment.server.allowAsset = (url) => !url.path.endsWith(".dart"); |
| + for (var server in environment.servers) { |
| + server.allowAsset = (url) => !url.path.endsWith(".dart"); |
| + } |
| } |
| /// This completer is used to keep pub running (by not completing) and |
| /// to pipe fatal errors to pub's top-level error-handling machinery. |
| var completer = new Completer(); |
| - environment.server.barback.errors.listen((error) { |
| + environment.barback.errors.listen((error) { |
| log.error(log.red("Build error:\n$error")); |
| }); |
| - environment.server.barback.results.listen((result) { |
| + environment.barback.results.listen((result) { |
| if (result.succeeded) { |
| // TODO(rnystrom): Report using growl/inotify-send where available. |
| log.message("Build completed ${log.green('successfully')}"); |
| @@ -95,28 +103,79 @@ class ServeCommand extends PubCommand { |
| if (!completer.isCompleted) completer.completeError(error, stackTrace); |
| }); |
| - environment.server.results.listen((result) { |
| - if (result.isSuccess) { |
| - log.message("${log.green('GET')} ${result.url.path} $_arrow " |
| - "${result.id}"); |
| - return; |
| - } |
| - |
| - var msg = "${log.red('GET')} ${result.url.path} $_arrow"; |
| - var error = result.error.toString(); |
| - if (error.contains("\n")) { |
| - log.message("$msg\n${prefixLines(error)}"); |
| - } else { |
| - log.message("$msg $error"); |
| - } |
| - }, onError: (error, [stackTrace]) { |
| - if (!completer.isCompleted) completer.completeError(error, stackTrace); |
| - }); |
| - |
| - log.message("Serving ${entrypoint.root.name} " |
| - "on http://$hostname:${environment.server.port}"); |
| + var directoryLength = environment.servers |
| + .map((server) => server.rootDirectory.length) |
| + .reduce(math.max); |
| + for (var server in environment.servers) { |
| + // Add three characters to account for "[", "/", and "]". |
| + var directoryPrefix = log.gray( |
| + padRight("[${server.rootDirectory}/]", directoryLength + 3)); |
|
Bob Nystrom
2014/02/19 00:36:52
Nit, but I think I'd prefer not showing the "/" he
nweiz
2014/02/19 01:25:58
Done.
|
| + server.results.listen((result) { |
| + if (result.isSuccess) { |
| + log.message("$directoryPrefix ${log.green('GET')} " |
| + "${result.url.path} $_arrow ${result.id}"); |
| + return; |
| + } |
| + |
| + var msg = "$directoryPrefix ${log.red('GET')} ${result.url.path} " |
| + "$_arrow"; |
| + var error = result.error.toString(); |
| + if (error.contains("\n")) { |
| + log.message("$msg\n${prefixLines(error)}"); |
| + } else { |
| + log.message("$msg $error"); |
| + } |
| + }, onError: (error, [stackTrace]) { |
| + if (completer.isCompleted) return; |
| + completer.completeError(error, stackTrace); |
| + }); |
| + |
| + log.message("Serving ${entrypoint.root.name} " |
| + "${padRight(server.rootDirectory + '/', directoryLength + 1)} " |
| + "on ${log.bold('http://$hostname:${server.port}')}"); |
| + } |
| return completer.future; |
| }); |
| } |
| + |
| + /// Returns the set of directories that will be served from servers exposed to |
| + /// the user. |
|
Bob Nystrom
2014/02/19 00:36:52
Document that it may throw a UsageException if the
nweiz
2014/02/19 01:25:58
Done.
|
| + Set<String> get _directoriesToServe { |
| + if (commandOptions.rest.isEmpty) { |
| + var directories = ['web', 'test'].where(dirExists).toSet(); |
| + if (directories.isNotEmpty) return directories; |
| + usageError('Neither "web/" nor "test/" exist to serve from.'); |
| + } |
| + |
| + var directories = commandOptions.rest.map(p.normalize).toSet(); |
| + var invalid = directories.where((dir) => !isBeneath(dir, '.')); |
| + if (invalid.isNotEmpty) { |
| + usageError("${_directorySentence(invalid, "isn't", "aren't")} in this " |
| + "package."); |
| + } |
| + |
| + var nonExistent = directories.where((dir) => !dirExists(dir)); |
| + if (nonExistent.isNotEmpty) { |
| + usageError("${_directorySentence(nonExistent, "doesn't", "don't")} " |
| + "exist."); |
| + } |
| + |
| + return directories; |
| + } |
| + |
| + /// Converts a list of [directoryNames] to a sentence. |
| + /// |
| + /// After the list of directories, [singularVerb] will be used if there is |
| + /// only one directory and [pluralVerb] will be used if there are more than |
| + /// one. |
| + String _directorySentence(Iterable<String> directoryNames, |
| + String singularVerb, String pluralVerb) { |
| + var directories = pluralize('Directory', directoryNames.length, |
| + plural: 'Directories'); |
| + var names = toSentence(ordered(directoryNames).map((dir) => '"$dir/"')); |
| + var verb = pluralize(singularVerb, directoryNames.length, |
| + plural: pluralVerb); |
| + return "$directories $names $verb"; |
| + } |
| } |