Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(277)

Side by Side Diff: sdk/lib/_internal/pub/lib/src/command/serve.dart

Issue 167103003: Support serving from multiple directories using "pub serve". (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library pub.command.serve; 5 library pub.command.serve;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:math' as math;
8 9
9 import 'package:barback/barback.dart'; 10 import 'package:barback/barback.dart';
11 import 'package:path/path.dart' as p;
10 12
11 import '../barback/build_environment.dart'; 13 import '../barback/build_environment.dart';
12 import '../barback/pub_package_provider.dart'; 14 import '../barback/pub_package_provider.dart';
13 import '../command.dart'; 15 import '../command.dart';
14 import '../exit_codes.dart' as exit_codes; 16 import '../exit_codes.dart' as exit_codes;
15 import '../io.dart'; 17 import '../io.dart';
16 import '../log.dart' as log; 18 import '../log.dart' as log;
17 import '../utils.dart'; 19 import '../utils.dart';
18 20
19 final _arrow = getSpecial('\u2192', '=>'); 21 final _arrow = getSpecial('\u2192', '=>');
20 22
21 /// Handles the `serve` pub command. 23 /// Handles the `serve` pub command.
22 class ServeCommand extends PubCommand { 24 class ServeCommand extends PubCommand {
23 String get description => "Run a local web development server."; 25 String get description =>
24 String get usage => "pub serve"; 26 'Run a local web development server.\n\n'
27 'By default, this serves "web/" and "test/", but an explicit list of \n'
28 'directories to serve can be provided as well.';
29 String get usage => "pub serve [directories...]";
30 final takesArguments = true;
25 31
26 PubPackageProvider _provider; 32 PubPackageProvider _provider;
27 33
28 String get hostname => commandOptions['hostname']; 34 String get hostname => commandOptions['hostname'];
29 35
30 /// `true` if Dart entrypoints should be compiled to JavaScript. 36 /// `true` if Dart entrypoints should be compiled to JavaScript.
31 bool get useDart2JS => commandOptions['dart2js']; 37 bool get useDart2JS => commandOptions['dart2js'];
32 38
33 /// The build mode. 39 /// The build mode.
34 BarbackMode get mode => new BarbackMode(commandOptions['mode']); 40 BarbackMode get mode => new BarbackMode(commandOptions['mode']);
35 41
36 ServeCommand() { 42 ServeCommand() {
37 commandParser.addOption('port', defaultsTo: '8080', 43 commandParser.addOption('port', defaultsTo: '8080',
38 help: 'The port to listen on.'); 44 help: 'The base port to listen on.');
39 45
40 // A hidden option for the tests to work around a bug in some of the OS X 46 // A hidden option for the tests to work around a bug in some of the OS X
41 // bots where "localhost" very rarely resolves to the IPv4 loopback address 47 // bots where "localhost" very rarely resolves to the IPv4 loopback address
42 // instead of IPv6 (or vice versa). The tests will always set this to 48 // instead of IPv6 (or vice versa). The tests will always set this to
43 // 127.0.0.1. 49 // 127.0.0.1.
44 commandParser.addOption('hostname', 50 commandParser.addOption('hostname',
45 defaultsTo: 'localhost', 51 defaultsTo: 'localhost',
46 hide: true); 52 hide: true);
47 commandParser.addFlag('dart2js', defaultsTo: true, 53 commandParser.addFlag('dart2js', defaultsTo: true,
48 help: 'Compile Dart to JavaScript.'); 54 help: 'Compile Dart to JavaScript.');
(...skipping 10 matching lines...) Expand all
59 } on FormatException catch (_) { 65 } on FormatException catch (_) {
60 log.error('Could not parse port "${commandOptions['port']}"'); 66 log.error('Could not parse port "${commandOptions['port']}"');
61 this.printUsage(); 67 this.printUsage();
62 return flushThenExit(exit_codes.USAGE); 68 return flushThenExit(exit_codes.USAGE);
63 } 69 }
64 70
65 var watcherType = commandOptions['force-poll'] ? 71 var watcherType = commandOptions['force-poll'] ?
66 WatcherType.POLLING : WatcherType.AUTO; 72 WatcherType.POLLING : WatcherType.AUTO;
67 73
68 return BuildEnvironment.create(entrypoint, hostname, port, mode, 74 return BuildEnvironment.create(entrypoint, hostname, port, mode,
69 watcherType, ["web"].toSet(), 75 watcherType, _directoriesToServe,
70 useDart2JS: useDart2JS).then((environment) { 76 useDart2JS: useDart2JS).then((environment) {
71 77
72 // In release mode, strip out .dart files since all relevant ones have 78 // In release mode, strip out .dart files since all relevant ones have
73 // been compiled to JavaScript already. 79 // been compiled to JavaScript already.
74 if (mode == BarbackMode.RELEASE) { 80 if (mode == BarbackMode.RELEASE) {
75 environment.server.allowAsset = (url) => !url.path.endsWith(".dart"); 81 for (var server in environment.servers) {
82 server.allowAsset = (url) => !url.path.endsWith(".dart");
83 }
76 } 84 }
77 85
78 /// This completer is used to keep pub running (by not completing) and 86 /// This completer is used to keep pub running (by not completing) and
79 /// to pipe fatal errors to pub's top-level error-handling machinery. 87 /// to pipe fatal errors to pub's top-level error-handling machinery.
80 var completer = new Completer(); 88 var completer = new Completer();
81 89
82 environment.server.barback.errors.listen((error) { 90 environment.barback.errors.listen((error) {
83 log.error(log.red("Build error:\n$error")); 91 log.error(log.red("Build error:\n$error"));
84 }); 92 });
85 93
86 environment.server.barback.results.listen((result) { 94 environment.barback.results.listen((result) {
87 if (result.succeeded) { 95 if (result.succeeded) {
88 // TODO(rnystrom): Report using growl/inotify-send where available. 96 // TODO(rnystrom): Report using growl/inotify-send where available.
89 log.message("Build completed ${log.green('successfully')}"); 97 log.message("Build completed ${log.green('successfully')}");
90 } else { 98 } else {
91 log.message("Build completed with " 99 log.message("Build completed with "
92 "${log.red(result.errors.length)} errors."); 100 "${log.red(result.errors.length)} errors.");
93 } 101 }
94 }, onError: (error, [stackTrace]) { 102 }, onError: (error, [stackTrace]) {
95 if (!completer.isCompleted) completer.completeError(error, stackTrace); 103 if (!completer.isCompleted) completer.completeError(error, stackTrace);
96 }); 104 });
97 105
98 environment.server.results.listen((result) { 106 var directoryLength = environment.servers
99 if (result.isSuccess) { 107 .map((server) => server.rootDirectory.length)
100 log.message("${log.green('GET')} ${result.url.path} $_arrow " 108 .reduce(math.max);
101 "${result.id}"); 109 for (var server in environment.servers) {
102 return; 110 // Add three characters to account for "[", "/", and "]".
103 } 111 var directoryPrefix = log.gray(
112 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.
113 server.results.listen((result) {
114 if (result.isSuccess) {
115 log.message("$directoryPrefix ${log.green('GET')} "
116 "${result.url.path} $_arrow ${result.id}");
117 return;
118 }
104 119
105 var msg = "${log.red('GET')} ${result.url.path} $_arrow"; 120 var msg = "$directoryPrefix ${log.red('GET')} ${result.url.path} "
106 var error = result.error.toString(); 121 "$_arrow";
107 if (error.contains("\n")) { 122 var error = result.error.toString();
108 log.message("$msg\n${prefixLines(error)}"); 123 if (error.contains("\n")) {
109 } else { 124 log.message("$msg\n${prefixLines(error)}");
110 log.message("$msg $error"); 125 } else {
111 } 126 log.message("$msg $error");
112 }, onError: (error, [stackTrace]) { 127 }
113 if (!completer.isCompleted) completer.completeError(error, stackTrace); 128 }, onError: (error, [stackTrace]) {
114 }); 129 if (completer.isCompleted) return;
130 completer.completeError(error, stackTrace);
131 });
115 132
116 log.message("Serving ${entrypoint.root.name} " 133 log.message("Serving ${entrypoint.root.name} "
117 "on http://$hostname:${environment.server.port}"); 134 "${padRight(server.rootDirectory + '/', directoryLength + 1)} "
135 "on ${log.bold('http://$hostname:${server.port}')}");
136 }
118 137
119 return completer.future; 138 return completer.future;
120 }); 139 });
121 } 140 }
141
142 /// Returns the set of directories that will be served from servers exposed to
143 /// 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.
144 Set<String> get _directoriesToServe {
145 if (commandOptions.rest.isEmpty) {
146 var directories = ['web', 'test'].where(dirExists).toSet();
147 if (directories.isNotEmpty) return directories;
148 usageError('Neither "web/" nor "test/" exist to serve from.');
149 }
150
151 var directories = commandOptions.rest.map(p.normalize).toSet();
152 var invalid = directories.where((dir) => !isBeneath(dir, '.'));
153 if (invalid.isNotEmpty) {
154 usageError("${_directorySentence(invalid, "isn't", "aren't")} in this "
155 "package.");
156 }
157
158 var nonExistent = directories.where((dir) => !dirExists(dir));
159 if (nonExistent.isNotEmpty) {
160 usageError("${_directorySentence(nonExistent, "doesn't", "don't")} "
161 "exist.");
162 }
163
164 return directories;
165 }
166
167 /// Converts a list of [directoryNames] to a sentence.
168 ///
169 /// After the list of directories, [singularVerb] will be used if there is
170 /// only one directory and [pluralVerb] will be used if there are more than
171 /// one.
172 String _directorySentence(Iterable<String> directoryNames,
173 String singularVerb, String pluralVerb) {
174 var directories = pluralize('Directory', directoryNames.length,
175 plural: 'Directories');
176 var names = toSentence(ordered(directoryNames).map((dir) => '"$dir/"'));
177 var verb = pluralize(singularVerb, directoryNames.length,
178 plural: pluralVerb);
179 return "$directories $names $verb";
180 }
122 } 181 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698