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

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, 5 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 return PubPackageProvider.create(entrypoint).then((provider) {
36 var port;
37 try {
38 port = int.parse(commandOptions['port']);
39 } on FormatException catch(_) {
40 log.error('Could not parse port "${commandOptions['port']}"');
41 this.printUsage();
42 exit(exit_codes.USAGE);
43 }
44
45 var barback = new Barback(provider);
46
47 barback.results.listen((result) {
48 if (result.succeeded) {
49 log.message("Build completed ${_green}successfully$_none");
50 } else {
51 log.message("Build completed with "
52 "${_red}${result.errors.length}$_none errors.");
53 }
54 });
55
56 barback.errors.listen((error) {
57 log.error("${_red}Build error:\n$error$_none");
58 });
59
60 // Add all of the visible files.
61 for (var package in provider.packages) {
62 barback.updateSources(provider.listAssets(package));
63 }
64 // TODO(rnystrom): Watch file system and update sources again when they
65 // are added or modified.
66
67 HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port).then((server) {
68 log.message("Serving ${entrypoint.root.name} "
69 "on http://localhost:${server.port}");
70
71 server.listen((request) {
72 var id = getIdFromUri(request.uri);
73 barback.getAssetById(id).then((asset) {
74 log.message(
75 "$_green${request.method}$_none ${request.uri} -> $asset");
76 // TODO(rnystrom): Set content-type based on asset type.
77 return request.response.addStream(asset.read()).then((_) {
78 request.response.close();
79 });
80 }).catchError((error) {
81 log.error("$_red${request.method}$_none ${request.uri} -> $error");
82 if (error is AssetNotFoundException) {
83 request.response.statusCode = 404;
84 request.response.reasonPhrase = "Not Found";
85 request.response.write(error);
86 } else {
87 request.response.statusCode = 500;
88 request.response.reasonPhrase = "Internal Server Error";
89 request.response.writeln(error);
90 request.response.writeln(getAttachedStackTrace(error));
91 }
nweiz 2013/07/29 20:23:15 Error responses should have the content-type set t
92 request.response.close();
93 });
94 });
95 });
96
97 // TODO(rnystrom): Hack! Return a future that never completes to leave
98 // pub running.
99 return new Completer().future;
100 });
101 }
102
103 AssetId getIdFromUri(Uri uri) {
104 var uriBuilder = new path.Builder(style: path.Style.url);
nweiz 2013/07/29 20:23:15 I think the pattern we usually use for this is a t
Bob Nystrom 2013/07/29 21:45:43 Added default builders for each style to path and
105 var parts = uriBuilder.split(uri.path);
106
107 // Strip the leading "/" from the URL.
108 parts.removeAt(0);
109
110 // Special case: if the URL is "/", map it to "web/index.html".
111 // TODO(rnystrom): What about directory URLs? Should "foo" or "foo/" map
112 // to "foo/index.html"?
113 if (parts.isEmpty) parts = ["index.html"];
114
115 // Checks to see if [uri]'s path contains a special directory [name] that
116 // identifies an asset within some package. If so, maps the package name
117 // and path following that to be within [dir] inside that package.
118 AssetId _trySpecialUrl(String name, String dir) {
119 // Find the package name and the relative path in the package.
120 var index = parts.indexOf(name);
121 if (index == -1) return null;
122 if (index + 1 >= parts.length) return null;
123
124 var package = parts[index + 1];
125 var assetPath = uriBuilder.join(dir,
126 uriBuilder.joinAll(parts.skip(index + 2)));
127 return new AssetId(package, assetPath);
128 }
nweiz 2013/07/29 20:23:15 Currently this won't 404 for "/packages" if there'
Bob Nystrom 2013/07/29 21:45:43 Made it do the right thing.
129
130 // See if it's "packages" URL.
131 var id = _trySpecialUrl("packages", "lib");
132 if (id != null) return id;
133
134 // See if it's an "assets" URL.
135 id = _trySpecialUrl("assets", "asset");
136 if (id != null) return id;
137
138 // Otherwise, it's a path in current package's web directory.
139 return new AssetId(entrypoint.root.name,
140 uriBuilder.join("web", uriBuilder.joinAll(parts)));
141 }
142 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698