Wire dart2js through flutter tool, add compilation test (#27668)

diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart
new file mode 100644
index 0000000..4dab748
--- /dev/null
+++ b/packages/flutter_tools/lib/src/web/compile.dart
@@ -0,0 +1,70 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:meta/meta.dart';
+
+import '../artifacts.dart';
+import '../base/common.dart';
+import '../base/context.dart';
+import '../base/file_system.dart';
+import '../base/io.dart';
+import '../base/process_manager.dart';
+import '../build_info.dart';
+import '../convert.dart';
+import '../globals.dart';
+
+/// The [WebCompiler] instance.
+WebCompiler get webCompiler => context[WebCompiler];
+
+/// A wrapper around dart2js for web compilation.
+class WebCompiler {
+  const WebCompiler();
+
+  /// Compile `target` using dart2js.
+  ///
+  /// `minify` controls whether minifaction of the source is enabled. Defaults to `true`.
+  /// `enabledAssertions` controls whether assertions are enabled. Defaults to `false`.
+  Future<int> compile({@required String target, bool minify = true, bool enabledAssertions = false}) async {
+    final String engineDartPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
+    final String dart2jsPath = artifacts.getArtifactPath(Artifact.dart2jsSnapshot);
+    final String flutterPatchedSdkPath = artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath);
+    final String librariesPath = fs.path.join(flutterPatchedSdkPath, 'libraries.json');
+    final Directory outputDir = fs.directory(getWebBuildDirectory());
+    if (!outputDir.existsSync()) {
+      outputDir.createSync(recursive: true);
+    }
+    final String outputPath = fs.path.join(outputDir.path, 'main.dart.js');
+    if (!processManager.canRun(engineDartPath)) {
+      throwToolExit('Unable to find Dart binary at $engineDartPath');
+    }
+    final List<String> command = <String>[
+      engineDartPath,
+      dart2jsPath,
+      target,
+      '-o',
+      '$outputPath',
+      '--libraries-spec=$librariesPath',
+      '--platform-binaries=$flutterPatchedSdkPath',
+    ];
+    if (minify) {
+      command.add('-m');
+    }
+    if (enabledAssertions) {
+      command.add('--enable-asserts');
+    }
+    printTrace(command.join(' '));
+    final Process result = await processManager.start(command);
+    result
+        .stdout
+        .transform(utf8.decoder)
+        .transform(const LineSplitter())
+        .listen(printStatus);
+    result
+        .stderr
+        .transform(utf8.decoder)
+        .transform(const LineSplitter())
+        .listen(printError);
+    return result.exitCode;
+  }
+}