Index: sdk/lib/_internal/pub/lib/src/command_deploy.dart |
diff --git a/sdk/lib/_internal/pub/lib/src/command_deploy.dart b/sdk/lib/_internal/pub/lib/src/command_deploy.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..94decb64df63846d96829362226a77ce8264b861 |
--- /dev/null |
+++ b/sdk/lib/_internal/pub/lib/src/command_deploy.dart |
@@ -0,0 +1,141 @@ |
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library command_deploy; |
+ |
+import 'dart:async'; |
+import 'dart:math' as math; |
+ |
+import 'package:analyzer_experimental/analyzer.dart'; |
+import 'package:pathos/path.dart' as path; |
+ |
+import 'command.dart'; |
+import 'dart.dart' as dart; |
+import 'io.dart'; |
+import 'log.dart' as log; |
+import 'utils.dart'; |
+ |
+final _arrow = getSpecial('\u2192', '=>'); |
+ |
+/// Handles the `deploy` pub command. |
+class DeployCommand extends PubCommand { |
+ final description = "Copy and compile all Dart entrypoints in the 'web' " |
+ "directory."; |
+ final usage = "pub deploy [options]"; |
+ final aliases = const ["settle-up"]; |
+ |
+ // TODO(nweiz): make these configurable. |
+ /// The path to the source directory of the deployment. |
+ String get source => path.join(entrypoint.root.dir, 'web'); |
+ |
+ /// The path to the target directory of the deployment. |
+ String get target => path.join(entrypoint.root.dir, 'deploy'); |
+ |
+ /// The set of Dart entrypoints in [source] that should be compiled to [out]. |
+ final _entrypoints = new List<String>(); |
+ |
+ int _maxVerbLength; |
+ int _maxSourceLength; |
+ |
+ Future onRun() { |
+ if (!dirExists(source)) { |
+ throw new ApplicationException("There is no '$source' directory."); |
+ } |
+ |
+ return entrypoint.packageFiles(beneath: source).then((files) { |
+ log.message("Finding entrypoints..."); |
+ _findEntrypoints(files); |
+ _computeLogSize(); |
+ |
+ cleanDir(target); |
+ _logAction("Copying", "${path.relative(source)}/", |
+ "${path.relative(target)}/"); |
+ copyFiles(files.where((file) => path.extension(file) != '.dart'), |
+ source, target); |
+ |
+ return Future.forEach(_entrypoints, (sourceFile) { |
+ var targetFile = |
+ path.join(target, path.relative(sourceFile, from: source)); |
+ var relativeTargetFile = path.relative(targetFile); |
+ var relativeSourceFile = path.relative(sourceFile); |
+ |
+ ensureDir(path.dirname(targetFile)); |
+ _logAction("Compiling", relativeSourceFile, "$relativeTargetFile.js"); |
+ // TODO(nweiz): print dart2js errors/warnings in red. |
+ return dart.compile(sourceFile, packageRoot: entrypoint.packagesDir) |
+ .then((js) { |
+ writeTextFile("$targetFile.js", js, dontLogContents: true); |
+ _logAction("Compiling", relativeSourceFile, "$relativeTargetFile"); |
+ return dart.compile(sourceFile, |
+ packageRoot: entrypoint.packagesDir, toDart: true); |
+ }).then((dart) { |
+ writeTextFile(targetFile, dart, dontLogContents: true); |
+ // TODO(nweiz): we should put dart.js files next to any HTML file |
+ // rather than any entrypoint. An HTML file could import an entrypoint |
+ // that's not adjacent. |
+ _maybeAddDartJs(path.dirname(targetFile)); |
+ }); |
+ }); |
+ }); |
+ } |
+ |
+ /// Populates [_entrypoints] with all of the Dart entrypoints in [files]. |
+ /// [files] should be a list of paths in [source]. |
+ void _findEntrypoints(List<String> files) { |
+ for (var file in files) { |
+ if (path.extension(file) != '.dart') continue; |
+ try { |
+ if (!dart.isEntrypoint(parseDartFile(file))) continue; |
+ } on AnalyzerErrorGroup catch (e) { |
+ log.warning(e.message); |
+ continue; |
+ } |
+ _entrypoints.add(file); |
+ } |
+ // Sort to ensure a deterministic order of compilation and output. |
+ _entrypoints.sort(); |
+ } |
+ |
+ /// Computes the maximum widths of words that will be used in log output for |
+ /// the deploy command so we know how much padding to add when printing them. |
+ /// This should only be run after [_findEntrypoints]. |
+ void _computeLogSize() { |
+ _maxVerbLength = ["Copying", "Compiling"] |
+ .map((verb) => verb.length).reduce(math.max); |
+ var sourceLengths = new List.from( |
+ _entrypoints.map((file) => path.relative(file).length)) |
+ ..add("${path.relative(source)}/".length); |
+ if (_shouldAddDartJs) sourceLengths.add("package:browser/dart.js".length); |
+ _maxSourceLength = sourceLengths.reduce(math.max); |
+ } |
+ |
+ /// Log a deployment action. This should only be run after [_computeLogSize]. |
+ void _logAction(String verb, String source, String target) { |
+ verb = padRight(verb, _maxVerbLength); |
+ source = padRight(source, _maxSourceLength); |
+ log.message("$verb $source $_arrow $target"); |
+ } |
+ |
+ // TODO(nweiz): do something more principled when issue 6101 is fixed. |
+ /// If this package depends non-transitively on the `browser` package, this |
+ /// ensures that the `dart.js` file is copied into [directory], under |
+ /// `packages/browser/`. |
+ void _maybeAddDartJs(String directory) { |
+ var jsPath = path.join(directory, 'packages', 'browser', 'dart.js'); |
+ // TODO(nweiz): warn if they don't depend on browser? |
+ if (!_shouldAddDartJs || fileExists(jsPath)) return; |
+ |
+ _logAction("Copying", "package:browser/dart.js", path.relative(jsPath)); |
+ ensureDir(path.dirname(jsPath)); |
+ copyFile(path.join(entrypoint.packagesDir, 'browser', 'dart.js'), jsPath); |
+ } |
+ |
+ /// Whether we should copy the browser package's dart.js file into the |
+ /// deployed app. |
+ bool get _shouldAddDartJs { |
+ return !_entrypoints.isEmpty && |
+ entrypoint.root.dependencies.any((dep) => |
+ dep.name == 'browser' && dep.source.name == 'hosted'); |
+ } |
+} |