Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 } | |
| OLD | NEW |