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

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

Issue 20204003: First stab at a dev server in pub using barback. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Revise. Created 7 years, 4 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library pub.command.serve;
6
7 import 'dart:async';
8 import 'dart:io';
9
10 import 'package:barback/barback.dart';
11 import 'package:path/path.dart' as path;
12
13 import '../command.dart';
14 import '../entrypoint.dart';
15 import '../exit_codes.dart' as exit_codes;
16 import '../log.dart' as log;
17 import '../pub_package_provider.dart';
18 import '../utils.dart';
19
20 final _green = getPlatformString('\u001b[32m');
21 final _red = getPlatformString('\u001b[31m');
22 final _none = getPlatformString('\u001b[0m');
23
24 /// Handles the `serve` pub command.
25 class ServeCommand extends PubCommand {
26 String get description => "Run a local web development server.";
27 String get usage => 'pub serve';
28
29 ServeCommand() {
30 commandParser.addOption('port', defaultsTo: '8080',
31 help: 'The port to listen on.');
32 }
33
34 Future onRun() {
35 // The completer for the top-level future returned by the command. Only
36 // used to keep pub running (by not completing) and to pipe fatal errors
37 // to pub's top-level error-handling machinery.
38 var completer = new Completer();
39
40 return PubPackageProvider.create(entrypoint).then((provider) {
41 var port;
42 try {
43 port = int.parse(commandOptions['port']);
44 } on FormatException catch(_) {
45 log.error('Could not parse port "${commandOptions['port']}"');
46 this.printUsage();
47 exit(exit_codes.USAGE);
48 }
49
50 var barback = new Barback(provider);
51
52 barback.results.listen((result) {
53 if (result.succeeded) {
54 // TODO(rnystrom): Report using growl/inotify-send where available.
55 log.message("Build completed ${_green}successfully$_none");
56 } else {
57 log.message("Build completed with "
58 "${_red}${result.errors.length}$_none errors.");
59 }
60 });
61
62 barback.errors.listen((error) {
63 log.error("${_red}Build error:\n$error$_none");
64 });
65
66 // TODO(rnystrom): Watch file system and update sources again when they
67 // are added or modified.
68
69 HttpServer.bind("localhost", port).then((server) {
70 log.message("Serving ${entrypoint.root.name} "
71 "on http://localhost:${server.port}");
72
73 // Add all of the visible files.
74 for (var package in provider.packages) {
75 barback.updateSources(provider.listAssets(package));
76 }
77
78 server.listen((request) {
79 var id = getIdFromUri(request.uri);
80 if (id == null) {
81 return notFound(request, "Path ${request.uri.path} is not valid.");
82 }
83
84 barback.getAssetById(id).then((asset) {
85 log.message(
86 "$_green${request.method}$_none ${request.uri} -> $asset");
87 // TODO(rnystrom): Set content-type based on asset type.
88 return request.response.addStream(asset.read()).then((_) {
89 request.response.close();
90 });
nweiz 2013/07/29 23:54:44 If we're not doing the 500 thing for errors coming
Bob Nystrom 2013/07/30 01:04:27 Done.
91 }).catchError((error) {
92 log.error("$_red${request.method}$_none ${request.uri} -> $error");
93 if (error is! AssetNotFoundException) {
94 completer.completeError(error);
95 return;
96 }
97
98 notFound(request, error);
99 });
100 });
101 });
102
103 return completer.future;
104 });
105 }
106
107 /// Responds to [request] with a 404 response and closes it.
108 void notFound(HttpRequest request, message) {
109 request.response.statusCode = 404;
110 request.response.reasonPhrase = "Not Found";
111 request.response.write(message);
112 request.response.close();
113 }
114
115 AssetId getIdFromUri(Uri uri) {
116 var parts = path.url.split(uri.path);
117
118 // Strip the leading "/" from the URL.
119 parts.removeAt(0);
120
121 var isSpecial = false;
122
123 // Checks to see if [uri]'s path contains a special directory [name] that
124 // identifies an asset within some package. If so, maps the package name
125 // and path following that to be within [dir] inside that package.
126 AssetId _trySpecialUrl(String name, String dir) {
127 // Find the package name and the relative path in the package.
128 var index = parts.indexOf(name);
129 if (index == -1) return null;
130
131 // If we got here, the path *did* contain the special directory, which
132 // means we should not interpret it as a regular path, even if it's
133 // missing the package name after it, which makes it invalid here.
134 isSpecial = true;
135 if (index + 1 >= parts.length) return null;
136
137 var package = parts[index + 1];
138 var assetPath = path.url.join(dir,
139 path.url.joinAll(parts.skip(index + 2)));
140 return new AssetId(package, assetPath);
141 }
142
143 // See if it's "packages" URL.
144 var id = _trySpecialUrl("packages", "lib");
145 if (id != null) return id;
146
147 // See if it's an "assets" URL.
148 id = _trySpecialUrl("assets", "asset");
149 if (id != null) return id;
150
151 // If we got here, we had a path like "/packages" which is a special
152 // directory, but not a valid path since it lacks a following package name.
153 if (isSpecial) return null;
154
155 // Otherwise, it's a path in current package's web directory.
156 return new AssetId(entrypoint.root.name,
157 path.url.join("web", path.url.joinAll(parts)));
158 }
159 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698