karma: Use upstream v4

Upgraded Karma version to latest, and implemented custom injector for
iblaze in @bazel/karma module.

Closes #387

PiperOrigin-RevId: 230791110
diff --git a/devserver/concatjs/BUILD.bazel b/devserver/concatjs/BUILD.bazel
new file mode 100644
index 0000000..4606773
--- /dev/null
+++ b/devserver/concatjs/BUILD.bazel
@@ -0,0 +1,14 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["concatjs.go"],
+    importpath = "github.com/bazelbuild/rules_typescript/devserver/concatjs",
+    visibility = ["//visibility:public"],
+)
+
+go_test(
+    name = "go_default_test",
+    srcs = ["concatjs_test.go"],
+    embed = [":go_default_library"],
+)
\ No newline at end of file
diff --git a/devserver/runfiles/BUILD.bazel b/devserver/runfiles/BUILD.bazel
new file mode 100644
index 0000000..2e0b9b7
--- /dev/null
+++ b/devserver/runfiles/BUILD.bazel
@@ -0,0 +1,17 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+# Gazelle by default tries to map the import for the Bazel runfile go library to a repository with
+# an auto-generated name. This does not work for us because the rules_go repository name is different.
+# This gazelle directive ensures that Gazelle resolves the import to the proper Bazel label.
+# Read more here: https://github.com/bazelbuild/bazel-gazelle#directives
+# gazelle:resolve go github.com/bazelbuild/rules_go/go/tools/bazel @io_bazel_rules_go//go/tools/bazel:go_default_library
+
+go_library(
+    name = "go_default_library",
+    srcs = ["runfiles.go"],
+    deps = [
+        "@io_bazel_rules_go//go/tools/bazel:go_default_library",
+    ],
+    importpath = "github.com/bazelbuild/rules_typescript/devserver/runfiles",
+    visibility = ["//visibility:public"],
+)
diff --git a/internal/e2e/package_karma/README.md b/internal/e2e/package_karma/README.md
index 2dfa4e2..a9c0254 100644
--- a/internal/e2e/package_karma/README.md
+++ b/internal/e2e/package_karma/README.md
@@ -1,5 +1,5 @@
 # Testing karma dependency
 
 A karma 3.0.0 dependency is here to verify that a user's karma dependency doesn't interfere with
-the ts_web_test_suite rule which depends on a transitive dependency in @bazel/karma on a fork
-of karma.
+the ts_web_test_suite rule which depends on a transitive dependency in @bazel/karma on a different
+version of karma.
diff --git a/internal/karma/BUILD.bazel b/internal/karma/BUILD.bazel
index 7852325..b2a22bc 100644
--- a/internal/karma/BUILD.bazel
+++ b/internal/karma/BUILD.bazel
@@ -13,7 +13,7 @@
 ])
 
 ts_library(
-    name = "karma_concat_js",
+    name = "bazel_karma",
     srcs = glob(["*.ts"]),
     module_name = "@bazel/karma",
     tsconfig = "//internal/karma:tsconfig.json",
@@ -26,7 +26,7 @@
 nodejs_binary(
     name = "karma_bin",
     data = [
-        ":karma_concat_js",
+        ":bazel_karma",
         "@npm//jasmine-core",
         "@npm//karma",
         "@npm//karma-chrome-launcher",
@@ -65,7 +65,7 @@
     ],
     deps = [
         ":check_version_copy",
-        ":karma_concat_js",
+        ":bazel_karma",
         ":license_copy",
     ],
 )
diff --git a/internal/karma/index.ts b/internal/karma/index.ts
index 74c4750..ad02ec9 100644
--- a/internal/karma/index.ts
+++ b/internal/karma/index.ts
@@ -6,6 +6,7 @@
 import * as path from 'path';
 import * as process from 'process';
 import * as tmp from 'tmp';
+import {createInterface} from 'readline';
 ///<reference types="lib.dom"/>
 
 /**
@@ -32,7 +33,8 @@
       path: '/concatjs_bundle.js',
       contentPath: tmpFile.name,
       isUrl: false,
-      content: ''
+      content: '',
+      encodings: {},
     } as any;
     const included = [];
 
@@ -70,6 +72,30 @@
 
 (initConcatJs as any).$inject = ['logger', 'emitter', 'config.basePath'];
 
+function watcher(fileList: {refresh: () => void}) {
+  // ibazel will write this string after a successful build
+  // We don't want to re-trigger tests if the compilation fails, so
+  // we should only listen for this event.
+  const IBAZEL_NOTIFY_BUILD_SUCCESS = 'IBAZEL_BUILD_COMPLETED SUCCESS';
+  // ibazel communicates with us via stdin
+  const rl = createInterface({input: process.stdin, terminal: false});
+  rl.on('line', (chunk: string) => {
+    if (chunk === IBAZEL_NOTIFY_BUILD_SUCCESS) {
+      fileList.refresh();
+    }
+  });
+  rl.on('close', () => {
+    // Give ibazel 5s to kill our process, otherwise do it ourselves
+    setTimeout(() => {
+      console.error('ibazel failed to stop karma after 5s; probably a bug');
+      process.exit(1);
+    }, 5000);
+  });
+}
+
+(watcher as any).$inject = ['fileList'];
+
 module.exports = {
-  'framework:concat_js': ['factory', initConcatJs]
+  'framework:concat_js': ['factory', initConcatJs],
+  'watcher': ['value', watcher],
 };
diff --git a/internal/karma/karma.conf.js b/internal/karma/karma.conf.js
index 1ef2a49..ff066d9 100644
--- a/internal/karma/karma.conf.js
+++ b/internal/karma/karma.conf.js
@@ -151,7 +151,7 @@
 
     // enable / disable watching file and executing tests whenever
     // any file changes
-    overrideConfigValue(conf, 'autoWatch', true);
+    overrideConfigValue(conf, 'autoWatch', process.env['IBAZEL_NOTIFY_CHANGES'] === 'y');
 
     // Continuous Integration mode
     // if true, Karma captures browsers, runs the tests and exits
@@ -165,14 +165,6 @@
     // base path that will be used to resolve all patterns
     // (eg. files, exclude)
     overrideConfigValue(conf, 'basePath', 'TMPL_runfiles_path');
-
-    if (process.env['IBAZEL_NOTIFY_CHANGES'] === 'y') {
-      // Tell karma to only listen for ibazel messages on stdin rather than
-      // watch all the input files This is from fork alexeagle/karma in the
-      // ibazel branch:
-      // https://github.com/alexeagle/karma/blob/576d262af50b10e63485b86aee99c5358958c4dd/lib/server.js#L172
-      overrideConfigValue(conf, 'watchMode', 'ibazel');
-    }
   }
 
   /**
diff --git a/internal/karma/package.json b/internal/karma/package.json
index 9ffa9c0..be1c6eb 100644
--- a/internal/karma/package.json
+++ b/internal/karma/package.json
@@ -5,8 +5,8 @@
   "license": "Apache-2.0",
   "version": "0.0.0-PLACEHOLDER",
   "keywords": [
-      "karma",
-      "bazel"
+    "karma",
+    "bazel"
   ],
   "main": "./index.js",
   "typings": "./index.d.ts",
@@ -15,13 +15,13 @@
   },
   "dependencies": {
     "jasmine-core": "2.8.0",
-    "karma": "alexeagle/karma#fa1a84ac881485b5657cb669e9b4e5da77b79f0a",
+    "karma": "^4.0.0",
     "karma-chrome-launcher": "2.2.0",
     "karma-firefox-launcher": "1.1.0",
     "karma-jasmine": "1.1.1",
+    "karma-requirejs": "1.1.0",
     "karma-sauce-launcher": "2.0.2",
     "karma-sourcemap-loader": "0.3.7",
-    "karma-requirejs": "1.1.0",
     "requirejs": "2.3.5",
     "semver": "5.6.0",
     "tmp": "0.0.33"