feat(labs): introduce a new ts_proto_library with grpc support

This version of ts_proto_library is generated using the more
standard grpc/grpc-web package.
diff --git a/WORKSPACE b/WORKSPACE
index 8ddd223..6fca652 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -297,3 +297,9 @@
 load("@npm_bazel_labs//:package.bzl", "npm_bazel_labs_dependencies")
 
 npm_bazel_labs_dependencies()
+
+load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
+
+rules_proto_dependencies()
+
+rules_proto_toolchains()
diff --git a/examples/protocol_buffers/BUILD.bazel b/examples/protocol_buffers/BUILD.bazel
index ddaf959..d5bd499 100644
--- a/examples/protocol_buffers/BUILD.bazel
+++ b/examples/protocol_buffers/BUILD.bazel
@@ -11,6 +11,14 @@
     srcs = ["tire.proto"],
 )
 
+ts_proto_library(
+    # The result will be "tire.d.ts" named after this target.
+    # We could use the output_name attribute if we want the output named
+    # differently than the target.
+    name = "tire",
+    proto = ":tire_proto",
+)
+
 proto_library(
     name = "car_proto",
     srcs = ["car.proto"],
@@ -22,7 +30,7 @@
     # We could use the output_name attribute if we want the output named
     # differently than the target.
     name = "car",
-    deps = [":car_proto"],
+    proto = ":car_proto",
 )
 
 ts_config(
@@ -38,16 +46,17 @@
     tsconfig = "//:tsconfig-test",
     deps = [
         ":car",
+        ":tire",
         "@npm//@types/jasmine",
-        "@npm//@types/long",
         "@npm//@types/node",
-        "@npm//long",
     ],
 )
 
 karma_web_test_suite(
     name = "test",
-    bootstrap = ["@npm_bazel_labs//protobufjs:bootstrap_scripts"],
+    srcs = [
+        "@npm_bazel_labs//grpc_web:bootstrap_scripts",
+    ],
     browsers = [
         "@io_bazel_rules_webtesting//browsers:chromium-local",
         "@io_bazel_rules_webtesting//browsers:firefox-local",
@@ -64,8 +73,8 @@
 
 ts_devserver(
     name = "devserver",
-    bootstrap = ["@npm_bazel_labs//protobufjs:bootstrap_scripts"],
     entry_module = "examples_protocol_buffers/app",
+    scripts = ["@npm_bazel_labs//grpc_web:bootstrap_scripts"],
     deps = [":app"],
 )
 
@@ -75,7 +84,12 @@
     config_file = "rollup.config.js",
     entry_point = ":app.ts",
     format = "iife",
-    deps = [":app"],
+    deps = [
+        ":app",
+        "@npm//:node_modules",
+        "@npm//rollup-plugin-commonjs",
+        "@npm//rollup-plugin-node-resolve",
+    ],
 )
 
 terser_minified(
@@ -83,27 +97,11 @@
     src = ":bundle",
 )
 
-# Needed because the prodserver only loads static files that appear under this
-# package.
-genrule(
-    name = "protobufjs",
-    srcs = [
-        "@build_bazel_rules_typescript_protobufs_compiletime_deps//:node_modules/protobufjs/dist/minimal/protobuf.min.js",
-        "@build_bazel_rules_typescript_protobufs_compiletime_deps//:node_modules/long/dist/long.js",
-    ],
-    outs = [
-        "protobuf.min.js",
-        "long.js",
-    ],
-    cmd = "outs=($(OUTS)); d=$$(dirname $${outs[0]}); for s in $(SRCS); do cp $$s $$d; done",
-)
-
 http_server(
     name = "prodserver",
     data = [
         "index.html",
         ":bundle.min",
-        ":protobufjs",
     ],
 )
 
diff --git a/examples/protocol_buffers/WORKSPACE b/examples/protocol_buffers/WORKSPACE
index f6cf1d1..0744852 100644
--- a/examples/protocol_buffers/WORKSPACE
+++ b/examples/protocol_buffers/WORKSPACE
@@ -32,6 +32,15 @@
     urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.11.4.tar.gz"],
 )
 
+http_archive(
+    name = "rules_proto",
+    sha256 = "4d421d51f9ecfe9bf96ab23b55c6f2b809cbaf0eea24952683e397decfbd0dd0",
+    strip_prefix = "rules_proto-f6b8d89b90a7956f6782a4a3609b2f0eee3ce965",
+    urls = [
+        "https://github.com/bazelbuild/rules_proto/archive/f6b8d89b90a7956f6782a4a3609b2f0eee3ce965.tar.gz",
+    ],
+)
+
 load("@build_bazel_rules_nodejs//:index.bzl", "yarn_install")
 
 yarn_install(
@@ -56,6 +65,12 @@
 
 npm_bazel_labs_dependencies()
 
+load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
+
+rules_proto_dependencies()
+
+rules_proto_toolchains()
+
 load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories")
 
 web_test_repositories()
diff --git a/examples/protocol_buffers/app.ts b/examples/protocol_buffers/app.ts
index 2308f5e..044b202 100644
--- a/examples/protocol_buffers/app.ts
+++ b/examples/protocol_buffers/app.ts
@@ -1,8 +1,9 @@
-import {Proto} from './car';
+import {Car} from 'examples_protocol_buffers/car_pb';
 
-const serverResponse = `{"make": "Porsche"}`;
-const car = Proto.Car.create(JSON.parse(serverResponse));
+const car = new Car();
+car.setMake('Porsche');
+
 const el: HTMLDivElement = document.createElement('div');
-el.innerText = `Car from server: ${car.make}`;
+el.innerText = `Car from server: ${car.getMake()}`;
 el.className = 'ts1';
 document.body.appendChild(el);
diff --git a/examples/protocol_buffers/car.spec.ts b/examples/protocol_buffers/car.spec.ts
index 65f90cd..6618fc1 100644
--- a/examples/protocol_buffers/car.spec.ts
+++ b/examples/protocol_buffers/car.spec.ts
@@ -1,34 +1,18 @@
-import {Proto} from './car';
-import Long = require('long');
+import {Car} from 'examples_protocol_buffers/car_pb';
+import {Tire} from 'examples_protocol_buffers/tire_pb';
 
 describe('protocol buffers', () => {
   it('allows creation of an object described by proto', () => {
-    const pontiac = Proto.Car.create({
-      make: 'pontiac',
-      frontTires: {
-        width: 225,
-        aspectRatio: 65,
-        construction: 'R',
-        diameter: 17,
-      },
-    });
-    expect(pontiac.make).toEqual('pontiac');
-    if (!pontiac.frontTires) {
-      fail('Should have frontTires set');
-    } else {
-      expect(pontiac.frontTires.width).toEqual(225);
-    }
-  });
+    const tires = new Tire();
+    tires.setAspectRatio(65);
+    tires.setWidth(225);
+    tires.setConstruction('R');
+    tires.setDiameter(17);
 
-  // Asserts that longs are handled correctly.
-  // This value comes from https://github.com/dcodeIO/long.js#background
-  it('handles long values correctly', () => {
-    const pontiac = Proto.Car.create({
-      make: 'pontiac',
-      // Long.MAX_VALUE
-      mileage: new Long(0xFFFFFFFF, 0x7FFFFFFF),
-    });
-    const object = Proto.Car.toObject(pontiac, {longs: String});
-    expect(object['mileage']).toEqual('9223372036854775807');
+    const pontiac = new Car();
+    pontiac.setMake('pontiac');
+    pontiac.setFrontTires(tires)
+
+    expect(pontiac.getMake()).toEqual('pontiac');
   });
 });
diff --git a/examples/protocol_buffers/index.html b/examples/protocol_buffers/index.html
index 6f8690e..e3c9871 100644
--- a/examples/protocol_buffers/index.html
+++ b/examples/protocol_buffers/index.html
@@ -3,8 +3,6 @@
     <title>protocol_buffers example</title>
 </head>
 <body>
-<script src="/protobuf.min.js"></script>
-<script src="/long.js"></script>
 <script src="/bundle.min.js"></script>
 </body>
 </html>
\ No newline at end of file
diff --git a/examples/protocol_buffers/package.json b/examples/protocol_buffers/package.json
index a2d4500..4bac109 100644
--- a/examples/protocol_buffers/package.json
+++ b/examples/protocol_buffers/package.json
@@ -9,6 +9,8 @@
     "@types/jasmine": "2.8.2",
     "@types/long": "^4.0.0",
     "@types/node": "11.11.1",
+    "google-protobuf": "3.11.4",
+    "grpc-web": "1.0.7",
     "http-server": "^0.11.1",
     "karma": "~4.1.0",
     "karma-chrome-launcher": "2.2.0",
@@ -21,6 +23,8 @@
     "protractor": "^5.4.2",
     "requirejs": "2.3.6",
     "rollup": "1.20.3",
+    "rollup-plugin-commonjs": "10.1.0",
+    "rollup-plugin-node-resolve": "5.2.0",
     "terser": "4.3.1",
     "typescript": "^3.3.1"
   },
diff --git a/examples/protocol_buffers/rollup.config.js b/examples/protocol_buffers/rollup.config.js
index c7bad73..acd7272 100644
--- a/examples/protocol_buffers/rollup.config.js
+++ b/examples/protocol_buffers/rollup.config.js
@@ -1,8 +1,9 @@
+const commonjs = require('rollup-plugin-commonjs');
+const nodeRequire = require('rollup-plugin-node-resolve');
+
 module.exports = {
-  // indicate which modules should be treated as external
-  external: [
-    'long',
-    'protobufjs/minimal',
+  plugins: [
+    nodeRequire(),
+    commonjs(),
   ],
-  output: {globals: {long: 'Long', 'protobufjs/minimal': 'protobuf'}}
-};
+};
\ No newline at end of file
diff --git a/examples/protocol_buffers/yarn.lock b/examples/protocol_buffers/yarn.lock
index 8902e36..fdc585a 100644
--- a/examples/protocol_buffers/yarn.lock
+++ b/examples/protocol_buffers/yarn.lock
@@ -107,6 +107,11 @@
   resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef"
   integrity sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==
 
+"@types/node@*":
+  version "13.9.0"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.0.tgz#5b6ee7a77faacddd7de719017d0bc12f52f81589"
+  integrity sha512-0ARSQootUG1RljH2HncpsY2TJBfGQIKOOi7kxzUY6z54ePu/ZD+wJA8zI2Q6v8rol2qpG/rvqsReco8zNMPvhQ==
+
 "@types/node@11.11.1":
   version "11.11.1"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.1.tgz#9ee55ffce20f72e141863b0036a6e51c6fc09a1f"
@@ -127,6 +132,13 @@
   resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5"
   integrity sha1-vShOV8hPEyXacCur/IKlMoGQwMU=
 
+"@types/resolve@0.0.8":
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194"
+  integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==
+  dependencies:
+    "@types/node" "*"
+
 "@types/selenium-webdriver@^3.0.0":
   version "3.0.16"
   resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.16.tgz#50a4755f8e33edacd9c406729e9b930d2451902a"
@@ -461,6 +473,11 @@
   resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
   integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
 
+builtin-modules@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484"
+  integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==
+
 bytebuffer@~5:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd"
@@ -924,6 +941,11 @@
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
   integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
 
+estree-walker@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
+  integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
+
 eventemitter3@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
@@ -1161,11 +1183,21 @@
     pify "^2.0.0"
     pinkie-promise "^2.0.0"
 
+google-protobuf@3.11.4:
+  version "3.11.4"
+  resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.11.4.tgz#598ca405a3cfa917a2132994d008b5932ef42014"
+  integrity sha512-lL6b04rDirurUBOgsY2+LalI6Evq8eH5TcNzi7TYQ3BsIWelT0KSOQSBsXuavEkNf+odQU6c0lgz3UsZXeNX9Q==
+
 graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
   version "4.2.2"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02"
   integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==
 
+grpc-web@1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/grpc-web/-/grpc-web-1.0.7.tgz#9e4fbcf63d3734515332ab59e42baa7d0d290015"
+  integrity sha512-Fkbz1nyvvt6GC6ODcxh9Fen6LLB3OTCgGHzHwM2Eni44SUhzqPz1UQgFp9sfBEfInOhx3yBdwo9ZLjZAmJ+TtA==
+
 har-schema@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
@@ -1465,6 +1497,11 @@
   dependencies:
     is-extglob "^2.1.1"
 
+is-module@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
+  integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
+
 is-number@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
@@ -1498,6 +1535,13 @@
   dependencies:
     isobject "^3.0.1"
 
+is-reference@^1.1.2:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.1.4.tgz#3f95849886ddb70256a3e6d062b1a68c13c51427"
+  integrity sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw==
+  dependencies:
+    "@types/estree" "0.0.39"
+
 is-regex@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
@@ -1769,6 +1813,13 @@
     pseudomap "^1.0.2"
     yallist "^2.1.2"
 
+magic-string@^0.25.2:
+  version "0.25.7"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
+  integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
+  dependencies:
+    sourcemap-codec "^1.4.4"
+
 map-cache@^0.2.2:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
@@ -2151,6 +2202,11 @@
   resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
   integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
 
+path-parse@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+  integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
 performance-now@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
@@ -2403,6 +2459,13 @@
   resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
   integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
 
+resolve@^1.11.0, resolve@^1.11.1:
+  version "1.15.1"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
+  integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
+  dependencies:
+    path-parse "^1.0.6"
+
 ret@~0.1.10:
   version "0.1.15"
   resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
@@ -2420,6 +2483,35 @@
   dependencies:
     glob "^7.1.3"
 
+rollup-plugin-commonjs@10.1.0:
+  version "10.1.0"
+  resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz#417af3b54503878e084d127adf4d1caf8beb86fb"
+  integrity sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==
+  dependencies:
+    estree-walker "^0.6.1"
+    is-reference "^1.1.2"
+    magic-string "^0.25.2"
+    resolve "^1.11.0"
+    rollup-pluginutils "^2.8.1"
+
+rollup-plugin-node-resolve@5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz#730f93d10ed202473b1fb54a5997a7db8c6d8523"
+  integrity sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==
+  dependencies:
+    "@types/resolve" "0.0.8"
+    builtin-modules "^3.1.0"
+    is-module "^1.0.0"
+    resolve "^1.11.1"
+    rollup-pluginutils "^2.8.1"
+
+rollup-pluginutils@^2.8.1:
+  version "2.8.2"
+  resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
+  integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==
+  dependencies:
+    estree-walker "^0.6.1"
+
 rollup@1.20.3:
   version "1.20.3"
   resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.20.3.tgz#6243f6c118ca05f56b2d9433112400cd834a1eb8"
@@ -2638,6 +2730,11 @@
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 
+sourcemap-codec@^1.4.4:
+  version "1.4.8"
+  resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
+  integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
+
 split-string@^3.0.1, split-string@^3.0.2:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
diff --git a/package.json b/package.json
index d426412..13a4d45 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,8 @@
         "conventional-changelog-cli": "^2.0.21",
         "core-util-is": "^1.0.2",
         "date-fns": "1.30.1",
+        "google-protobuf": "^3.6.1",
+        "grpc-web": "^1.0.7",
         "hello": "file:./tools/npm_packages/hello",
         "history-server": "^1.3.1",
         "http-server": "^0.11.1",
diff --git a/packages/labs/src/BUILD.bazel b/packages/labs/src/BUILD.bazel
index 8a75244..25bafdd 100644
--- a/packages/labs/src/BUILD.bazel
+++ b/packages/labs/src/BUILD.bazel
@@ -17,14 +17,13 @@
 # The generated `@bazel/labs` npm package contains a trimmed BUILD file using INTERNAL fences.
 package(default_visibility = ["//visibility:public"])
 
-exports_files(["tsconfig.json"])
-
 filegroup(
     name = "package_contents",
     srcs = glob(["*.bzl"]) + [
         "BUILD.bazel",
         "README.md",
         "package.json",
+        "//grpc_web:package_contents",
         "//protobufjs:package_contents",
     ],
 )
diff --git a/packages/labs/src/grpc_web/BUILD.bazel b/packages/labs/src/grpc_web/BUILD.bazel
new file mode 100644
index 0000000..e6aca6d
--- /dev/null
+++ b/packages/labs/src/grpc_web/BUILD.bazel
@@ -0,0 +1,32 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
+load("@build_bazel_rules_nodejs//internal/js_library:js_library.bzl", "js_library")
+
+package(default_visibility = ["//visibility:public"])
+
+bzl_library(
+    name = "bzl",
+    srcs = glob(["*.bzl"]),
+    visibility = ["//:__pkg__"],
+)
+
+nodejs_binary(
+    name = "change_import_style",
+    entry_point = ":change_import_style.js",
+    node_modules = "@build_bazel_rules_typescript_grpc_web_compiletime_deps//:node_modules",
+    visibility = ["//visibility:public"],
+)
+
+js_library(
+    name = "bootstrap_scripts",
+    srcs = [
+        "@npm//google-protobuf:google-protobuf__umd",
+        "@npm//grpc-web:grpc-web__umd",
+    ],
+)
+
+filegroup(
+    name = "package_contents",
+    srcs = glob(["*"]),
+    visibility = ["//:__pkg__"],
+)
diff --git a/packages/labs/src/grpc_web/README.md b/packages/labs/src/grpc_web/README.md
new file mode 100644
index 0000000..e5dc367
--- /dev/null
+++ b/packages/labs/src/grpc_web/README.md
@@ -0,0 +1,82 @@
+# ts_proto_library
+
+Bazel rule for generating TypeScript declarations for JavaScript protocol buffers 
+and GRPC Web service definitions using the [grpc-web](https://github.com/grpc/grpc-web)
+protoc plugin.
+
+## Getting Started
+
+Before you can use `ts_proto_library`, you must first setup:
+
+- [rules_proto](https://github.com/bazelbuild/rules_proto)
+- [rules_nodejs](https://github.com/bazelbuild/rules_nodejs)
+
+Once those are setup, add the following to your workspace:
+
+```python
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+# TODO: Setup rules_proto
+# TODO: Setup rules_nodejs
+
+load("@npm_bazel_labs//:package.bzl", "npm_bazel_labs_dependencies")
+
+npm_bazel_labs_dependencies()
+```
+
+Then, in your `BUILD` file:
+
+```python
+load("@rules_proto//:index.bzl", "typescript_proto_library")
+load("@npm_bazel_labs//:index.bzl", "ts_proto_library")
+
+proto_library(
+  name = "test_proto",
+  srcs = [
+    "test.proto",
+  ],
+)
+
+ts_proto_library(
+  name = "test_ts_proto",
+  proto = ":test_proto",
+)
+```
+
+You can now use the `test_ts_proto` target as a `dep` in other `ts_library` targets. However, you will need to include the following dependencies at runtime yourself:
+
+- `google-protobuf`
+- `grpc-web`
+
+UMD versions of these runtime dependencies are provided by `@npm_bazel_labs//grpc_web:bootstrap_scripts` (for use within `ts_devserver` and `karma_web_test_suite`)
+
+See `//examples/protocol_buffers/BUILD.bazel` for an example.
+
+## IDE Code Completion
+
+To get code completion working for the generated protos in your IDE, add the following to your
+`tsconfig.json`:
+
+```js
+{
+  "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+      // Replace <workspace-name> with the name of your workspace
+      "<workspace-name>/*": [
+        "*", // Enables absolute paths for src files in your project
+        "bazel-bin/*" // Enables referencing generate protos with absolute paths
+      ]
+    }
+  }
+}
+```
+
+## Implementation Details
+A bazel aspect is used to generate `ts_library` compatible output for all transitive
+dependencies of the proto passed to `ts_proto_library`.
+
+In its current state (as of March 15, 2020) the `grpc` protoc plugin is not fully capable of
+producing typescript source files. It is however, capable of generating type declarations and
+`commonjs` implementations. To make these output's compatible with `ts_library` the generated
+`commonjs` output is transformed into both ES5 UMD module files and ES6 module files.
diff --git a/packages/labs/src/grpc_web/change_import_style.js b/packages/labs/src/grpc_web/change_import_style.js
new file mode 100644
index 0000000..f1a285d
--- /dev/null
+++ b/packages/labs/src/grpc_web/change_import_style.js
@@ -0,0 +1,151 @@
+/**
+ * Converts a list of generated protobuf-js files from commonjs modules into named AMD modules.
+ *
+ * Initial implementation derived from
+ * https://github.com/Dig-Doug/rules_typescript_proto/blob/master/src/change_import_style.ts
+ *
+ * Arguments:
+ *   --workspace_name
+ *   --input_base_path
+ *   --output_module_name
+ *   --input_file_path
+ *   --output_file_path
+ */
+const minimist = require('minimist');
+const fs = require('fs');
+
+function main() {
+  const args = minimist(process.argv.slice(2));
+
+  /**
+   * Proto files with RPC service definitions will produce an extra file. During a bazel aspect we
+   * have to declare our outputs without knowing the contents of the proto file so we generate an
+   * empty stub for files without service definitions.
+   */
+  if (!fs.existsSync(args.input_file_path)) {
+    fs.writeFileSync(args.input_file_path, '', 'utf8');
+    fs.writeFileSync(args.input_file_path.replace('.js', '.d.ts'), '', 'utf8');
+    fs.writeFileSync(args.output_umd_path, '', 'utf8');
+    fs.writeFileSync(args.output_es6_path, '', 'utf8');
+  }
+
+  const initialContents = fs.readFileSync(args.input_file_path, 'utf8');
+
+  const umdContents = convertToUmd(args, initialContents);
+  fs.writeFileSync(args.output_umd_path, umdContents, 'utf8');
+
+  const commonJsContents = convertToESM(args, initialContents);
+  fs.writeFileSync(args.output_es6_path, commonJsContents, 'utf8');
+}
+
+function replaceRecursiveFilePaths(args) {
+  return (contents) => {
+    return contents.replace(/(\.\.\/)+/g, `${args.workspace_name}/`);
+  };
+}
+
+function removeJsExtensionsFromRequires(contents) {
+  return contents.replace(/(require\(.*).js/g, (_, captureGroup) => {
+    return captureGroup;
+  });
+}
+
+function convertToUmd(args, initialContents) {
+  const wrapInAMDModule = (contents) => {
+    return `// GENERATED CODE DO NOT EDIT
+    (function (factory) {
+      if (typeof module === "object" && typeof module.exports === "object") {
+        var v = factory(require, exports);
+        if (v !== undefined) module.exports = v;
+      }
+      else if (typeof define === "function" && define.amd) {
+        define("${args.input_base_path}/${args.output_module_name}",  factory);
+      }
+    })(function (require, exports) {
+      ${contents.replace(/module.exports =/g, 'return')}
+    });
+`;
+  };
+
+  const transformations =
+      [wrapInAMDModule, replaceRecursiveFilePaths(args), removeJsExtensionsFromRequires];
+  return transformations.reduce((currentContents, transform) => {
+    return transform(currentContents);
+  }, initialContents);
+}
+
+// Converts the CommonJS format from protoc to the ECMAScript Module format.
+// Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
+function convertToESM(args, initialContents) {
+  const replaceGoogExtendWithExports = (contents) => {
+    return contents.replace(/goog\.object\.extend\(exports, ([\w\.]+)\);/g, (_, packageName) => {
+      const exportSymbols = /goog\.exportSymbol\('([\w\.]+)',.*\);/g;
+      const symbols = [];
+
+      let match;
+      while ((match = exportSymbols.exec(initialContents))) {
+        // We want to ignore embedded export targets, IE:
+        // `DeliveryPerson.DataCase`.
+        const exportTarget = match[1].substr(packageName.length + 1);
+        if (!exportTarget.includes('.')) {
+          symbols.push(exportTarget);
+        }
+      }
+
+      return `export const { ${symbols.join(', ')} } = ${packageName}`;
+    });
+  };
+
+  const replaceCMDefaultExportWithExports = (contents) => {
+    return contents.replace(/module.exports = ([\w\.]+)\;/g, (_, packageName) => {
+      const exportSymbols = new RegExp(`${packageName.replace('.', '\\.')}\.([\\w\\.]+) =`, 'g');
+
+      const symbols = [];
+
+      let match;
+      while ((match = exportSymbols.exec(initialContents))) {
+        // We want to ignore embedded export targets, IE:
+        // `DeliveryPerson.DataCase`.
+        const exportTarget = match[1];
+        if (!exportTarget.includes('.')) {
+          symbols.push(exportTarget);
+        }
+      }
+
+      return `export const { ${symbols.join(', ')} } = ${packageName};`;
+    });
+  };
+
+  const replaceRequiresWithImports = (contents) => {
+    return contents
+        .replace(
+            /var ([\w\d_]+) = require\((['"][\.\\]*[\w\d@/_-]+['"])\)/g, 'import * as $1 from $2')
+        .replace(
+            /([\.\w\d_]+) = require\((['"][\.\w\d@/_-]+['"])\)/g, (_, variable, importPath) => {
+              const normalizedVariable = variable.replace(/\./g, '_');
+              return `import * as ${normalizedVariable} from ${importPath};\n${variable} = {...${
+                  normalizedVariable}}`;
+            });
+  };
+
+  const replaceRequiresWithSubpackageImports = (contents) => {
+    return contents.replace(
+        /var ([\w\d_]+) = require\((['"][\w\d@/_-]+['"])\)\.([\w\d_]+);/g,
+        'import * as $1 from $2;');
+  };
+
+  const replaceCJSExportsWithECMAExports = (contents) => {
+    return contents.replace(/exports\.([\w\d_]+) = .*;/g, 'export { $1 };');
+  };
+
+  const transformations = [
+    replaceRecursiveFilePaths(args), removeJsExtensionsFromRequires, replaceGoogExtendWithExports,
+    replaceRequiresWithImports, replaceRequiresWithSubpackageImports,
+    replaceCMDefaultExportWithExports, replaceCJSExportsWithECMAExports
+  ];
+  return transformations.reduce((currentContents, transform) => {
+    return transform(currentContents);
+  }, initialContents);
+}
+
+main();
diff --git a/packages/labs/src/grpc_web/package.json b/packages/labs/src/grpc_web/package.json
new file mode 100644
index 0000000..a94543c
--- /dev/null
+++ b/packages/labs/src/grpc_web/package.json
@@ -0,0 +1,9 @@
+{
+    "name": "grpc_web",
+    "version": "1.0.0",
+    "license": "MIT",
+    "dependencies": {
+        "minimist": "1.2.0"
+    }
+  }
+  
\ No newline at end of file
diff --git a/packages/labs/src/grpc_web/ts_proto_library.bzl b/packages/labs/src/grpc_web/ts_proto_library.bzl
new file mode 100644
index 0000000..bec8677
--- /dev/null
+++ b/packages/labs/src/grpc_web/ts_proto_library.bzl
@@ -0,0 +1,283 @@
+# Copyright 2020 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"Protocol Buffers"
+
+load("@build_bazel_rules_nodejs//:providers.bzl", "DeclarationInfo", "JSEcmaScriptModuleInfo", "JSNamedModuleInfo")
+load("@rules_proto//proto:defs.bzl", "ProtoInfo")
+
+typescript_proto_library_aspect = provider(
+    fields = {
+        "deps_dts": "The transitive dependencies' TS definitions",
+        "deps_es5": "The transitive ES5 JS dependencies",
+        "deps_es6": "The transitive ES6 JS dependencies",
+        "dts_outputs": "Ths TS definition files produced directly from the src protos",
+        "es5_outputs": "The ES5 JS files produced directly from the src protos",
+        "es6_outputs": "The ES6 JS files produced directly from the src protos",
+    },
+)
+
+# TODO(dan): Replace with |proto_common.direct_source_infos| when
+# https://github.com/bazelbuild/rules_proto/pull/22 lands.
+# Derived from https://github.com/grpc-ecosystem/grpc-gateway/blob/e8db07a3923d3f5c77dbcea96656afe43a2757a8/protoc-gen-swagger/defs.bzl#L11
+def _direct_source_infos(proto_info, provided_sources = []):
+    """Returns sequence of `ProtoFileInfo` for `proto_info`'s direct sources.
+    Files that are both in `proto_info`'s direct sources and in
+    `provided_sources` are skipped. This is useful, e.g., for well-known
+    protos that are already provided by the Protobuf runtime.
+    Args:
+      proto_info: An instance of `ProtoInfo`.
+      provided_sources: Optional. A sequence of files to ignore.
+          Usually, these files are already provided by the
+          Protocol Buffer runtime (e.g. Well-Known protos).
+    Returns: A sequence of `ProtoFileInfo` containing information about
+        `proto_info`'s direct sources.
+    """
+
+    source_root = proto_info.proto_source_root
+    if "." == source_root:
+        return [struct(file = src, import_path = src.path) for src in proto_info.direct_sources]
+
+    offset = len(source_root) + 1  # + '/'.
+
+    infos = []
+    for src in proto_info.direct_sources:
+        infos.append(struct(file = src, import_path = src.path[offset:]))
+
+    return infos
+
+def _get_protoc_inputs(target, ctx):
+    inputs = []
+    inputs += target[ProtoInfo].direct_sources
+    inputs += target[ProtoInfo].transitive_descriptor_sets.to_list()
+    return inputs
+
+def _build_protoc_command(target, ctx):
+    protoc_command = "%s" % (ctx.executable._protoc.path)
+
+    protoc_command += " --plugin=protoc-gen-grpc-web=%s" % (ctx.executable._protoc_gen_grpc_web.path)
+
+    protoc_output_dir = ctx.var["BINDIR"]
+    protoc_command += " --grpc-web_out=import_style=commonjs+dts,mode=grpcweb:%s" % (protoc_output_dir)
+    protoc_command += " --js_out=import_style=commonjs:%s" % (protoc_output_dir)
+
+    descriptor_sets_paths = [desc.path for desc in target[ProtoInfo].transitive_descriptor_sets.to_list()]
+
+    pathsep = ctx.configuration.host_path_separator
+    protoc_command += " --descriptor_set_in=\"%s\"" % (pathsep.join(descriptor_sets_paths))
+
+    proto_file_infos = _direct_source_infos(target[ProtoInfo])
+    for f in proto_file_infos:
+        protoc_command += " %s" % f.import_path
+
+    return protoc_command
+
+def _create_post_process_command(target, ctx, js_outputs, js_outputs_es6):
+    """
+    Builds a post-processing command that:
+      - Updates the existing protoc output files to be UMD modules
+      - Creates a new es6 file from the original protoc output
+    """
+    convert_commands = []
+    for [output, output_es6] in zip(js_outputs, js_outputs_es6):
+        file_path = "/".join([p for p in [
+            ctx.workspace_name,
+            ctx.label.package,
+        ] if p])
+        file_name = output.basename[:-len(output.extension) - 1]
+
+        convert_command = ctx.executable._change_import_style.path
+        convert_command += " --workspace_name={}".format(ctx.workspace_name)
+        convert_command += " --input_base_path={}".format(file_path)
+        convert_command += " --output_module_name={}".format(file_name)
+        convert_command += " --input_file_path={}".format(output.path)
+        convert_command += " --output_umd_path={}".format(output.path)
+        convert_command += " --output_es6_path={}".format(output_es6.path)
+        convert_commands.append(convert_command)
+
+    return " && ".join(convert_commands)
+
+def _get_outputs(target, ctx):
+    """
+    Calculates all of the files that will be generated by the aspect.
+    """
+    js_outputs = []
+    js_outputs_es6 = []
+    dts_outputs = []
+    for src in target[ProtoInfo].direct_sources:
+        file_name = src.basename[:-len(src.extension) - 1]
+        generated_files = ["_pb", "_grpc_web_pb"]
+        for f in generated_files:
+            full_name = file_name + f
+            output = ctx.actions.declare_file(full_name + ".js")
+            js_outputs.append(output)
+            output_es6 = ctx.actions.declare_file(full_name + ".mjs")
+            js_outputs_es6.append(output_es6)
+            output_d_ts = ctx.actions.declare_file(file_name + f + ".d.ts")
+            dts_outputs.append(output_d_ts)
+
+    return [js_outputs, js_outputs_es6, dts_outputs]
+
+def ts_proto_library_aspect_(target, ctx):
+    """
+    A bazel aspect that is applied on every proto_library rule on the transitive set of dependencies
+    of a ts_proto_library rule.
+
+    Handles running protoc to produce the generated JS and TS files.
+    """
+
+    [js_outputs, js_outputs_es6, dts_outputs] = _get_outputs(target, ctx)
+    protoc_outputs = dts_outputs + js_outputs + js_outputs_es6
+
+    all_commands = [
+        _build_protoc_command(target, ctx),
+        _create_post_process_command(target, ctx, js_outputs, js_outputs_es6),
+    ]
+
+    tools = []
+    tools.extend(ctx.files._protoc)
+    tools.extend(ctx.files._protoc_gen_grpc_web)
+    tools.extend(ctx.files._change_import_style)
+
+    ctx.actions.run_shell(
+        inputs = depset(
+            direct = _get_protoc_inputs(target, ctx),
+            transitive = [depset(ctx.files._well_known_protos)],
+        ),
+        outputs = protoc_outputs,
+        progress_message = "Creating Typescript pb files %s" % ctx.label,
+        command = " && ".join(all_commands),
+        tools = depset(tools),
+    )
+
+    dts_outputs = depset(dts_outputs)
+    es5_outputs = depset(js_outputs)
+    es6_outputs = depset(js_outputs_es6)
+    deps_dts = []
+    deps_es5 = []
+    deps_es6 = []
+
+    for dep in ctx.rule.attr.deps:
+        aspect_data = dep[typescript_proto_library_aspect]
+        deps_dts.append(aspect_data.dts_outputs)
+        deps_dts.append(aspect_data.deps_dts)
+        deps_es5.append(aspect_data.es5_outputs)
+        deps_es5.append(aspect_data.deps_es5)
+        deps_es6.append(aspect_data.es6_outputs)
+        deps_es6.append(aspect_data.deps_es6)
+
+    return [typescript_proto_library_aspect(
+        dts_outputs = dts_outputs,
+        es5_outputs = es5_outputs,
+        es6_outputs = es6_outputs,
+        deps_dts = depset(transitive = deps_dts),
+        deps_es5 = depset(transitive = deps_es5),
+        deps_es6 = depset(transitive = deps_es6),
+    )]
+
+ts_proto_library_aspect = aspect(
+    implementation = ts_proto_library_aspect_,
+    attr_aspects = ["deps"],
+    attrs = {
+        "_change_import_style": attr.label(
+            executable = True,
+            cfg = "host",
+            allow_files = True,
+            default = Label("@npm_bazel_labs//grpc_web:change_import_style"),
+        ),
+        "_protoc": attr.label(
+            allow_single_file = True,
+            executable = True,
+            cfg = "host",
+            default = Label("@com_google_protobuf//:protoc"),
+        ),
+        "_protoc_gen_grpc_web": attr.label(
+            allow_files = True,
+            executable = True,
+            cfg = "host",
+            default = Label("@com_github_grpc_grpc_web//javascript/net/grpc/web:protoc-gen-grpc-web"),
+        ),
+        "_well_known_protos": attr.label(
+            default = "@com_google_protobuf//:well_known_protos",
+            allow_files = True,
+        ),
+    },
+)
+
+def _ts_proto_library_impl(ctx):
+    """
+    Handles converting the aspect output into a provider compatible with the rules_typescript rules.
+    """
+    aspect_data = ctx.attr.proto[typescript_proto_library_aspect]
+    dts_outputs = aspect_data.dts_outputs
+    transitive_declarations = depset(transitive = [dts_outputs, aspect_data.deps_dts])
+    es5_outputs = aspect_data.es5_outputs
+    es6_outputs = aspect_data.es6_outputs
+    outputs = depset(transitive = [es5_outputs, es6_outputs, dts_outputs])
+
+    es5_srcs = depset(transitive = [es5_outputs, aspect_data.deps_es5])
+    es6_srcs = depset(transitive = [es6_outputs, aspect_data.deps_es6])
+    return struct(
+        typescript = struct(
+            declarations = dts_outputs,
+            transitive_declarations = transitive_declarations,
+            es5_sources = es5_srcs,
+            es6_sources = es6_srcs,
+            transitive_es5_sources = es5_srcs,
+            transitive_es6_sources = es6_srcs,
+        ),
+        providers = [
+            DefaultInfo(files = outputs),
+            DeclarationInfo(
+                declarations = dts_outputs,
+                transitive_declarations = transitive_declarations,
+                type_blacklisted_declarations = depset([]),
+            ),
+            JSNamedModuleInfo(
+                direct_sources = es5_srcs,
+                sources = es5_srcs,
+            ),
+            JSEcmaScriptModuleInfo(
+                direct_sources = es6_srcs,
+                sources = es6_srcs,
+            ),
+        ],
+    )
+
+ts_proto_library = rule(
+    attrs = {
+        "proto": attr.label(
+            allow_single_file = True,
+            aspects = [ts_proto_library_aspect],
+            mandatory = True,
+            providers = [ProtoInfo],
+        ),
+        "_protoc": attr.label(
+            allow_single_file = True,
+            cfg = "host",
+            default = Label("@com_google_protobuf//:protoc"),
+            executable = True,
+        ),
+        "_protoc_gen_grpc_web": attr.label(
+            allow_files = True,
+            cfg = "host",
+            default = Label("@com_github_grpc_grpc_web//javascript/net/grpc/web:protoc-gen-grpc-web"),
+            executable = True,
+        ),
+        "_well_known_protos": attr.label(
+            allow_files = True,
+            default = "@com_google_protobuf//:well_known_protos",
+        ),
+    },
+    implementation = _ts_proto_library_impl,
+)
diff --git a/packages/labs/src/grpc_web/yarn.lock b/packages/labs/src/grpc_web/yarn.lock
new file mode 100644
index 0000000..f312ae1
--- /dev/null
+++ b/packages/labs/src/grpc_web/yarn.lock
@@ -0,0 +1,8 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+minimist@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+  integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
diff --git a/packages/labs/src/index.bzl b/packages/labs/src/index.bzl
index a3cc31d..4e108cd 100644
--- a/packages/labs/src/index.bzl
+++ b/packages/labs/src/index.bzl
@@ -15,6 +15,8 @@
 """Public API surface is re-exported here.
 """
 
-load("//protobufjs:ts_proto_library.bzl", _ts_proto_library = "ts_proto_library")
+load("@npm_bazel_labs//grpc_web:ts_proto_library.bzl", _ts_proto_library = "ts_proto_library")
+load("@npm_bazel_labs//protobufjs:ts_proto_library.bzl", _protobufjs_ts_library = "ts_proto_library")
 
 ts_proto_library = _ts_proto_library
+protobufjs_ts_library = _protobufjs_ts_library
diff --git a/packages/labs/src/mock_io_bazel_rules_closure.bzl b/packages/labs/src/mock_io_bazel_rules_closure.bzl
new file mode 100644
index 0000000..ce4e04b
--- /dev/null
+++ b/packages/labs/src/mock_io_bazel_rules_closure.bzl
@@ -0,0 +1,28 @@
+"Mock transitive toolchain dependencies that are uneeded"
+
+def mock_io_bazel_rules_closure_impl(repository_ctx):
+    repository_ctx.file(
+        "closure/BUILD.bazel",
+        content = """\
+package(default_visibility = ["//visibility:public"])
+
+exports_files(["defs.bzl"])
+""",
+    )
+    repository_ctx.file(
+        "closure/defs.bzl",
+        content = """\
+"Minimal implementation to make loading phase succeed"
+
+def noop(**kwargs):
+    pass
+
+closure_js_binary = noop
+closure_js_library = noop
+closure_js_test = noop
+""",
+    )
+
+mock_io_bazel_rules_closure = repository_rule(
+    mock_io_bazel_rules_closure_impl,
+)
diff --git a/packages/labs/src/package.bzl b/packages/labs/src/package.bzl
index 636bc63..bcb71d4 100644
--- a/packages/labs/src/package.bzl
+++ b/packages/labs/src/package.bzl
@@ -1,8 +1,62 @@
 "Install toolchain dependencies"
 
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 load("@build_bazel_rules_nodejs//:index.bzl", "yarn_install")
+load(":mock_io_bazel_rules_closure.bzl", "mock_io_bazel_rules_closure")
 
 def npm_bazel_labs_dependencies():
+    """
+    Fetch our transitive dependencies.
+
+    If the user wants to get a different version of these, they can just fetch it
+    from their WORKSPACE before calling this function, or not call this function at all.
+    """
+
+    _maybe(
+        http_archive,
+        name = "com_github_grpc_grpc_web",
+        sha256 = "04460e28ffa80bfc797a8758da10ba40107347ef0af8e9cc065ade10398da4bb",
+        strip_prefix = "grpc-web-1.0.7",
+        urls = [
+            "https://github.com/grpc/grpc-web/archive/1.0.7.tar.gz",
+        ],
+    )
+
+    _maybe(
+        http_archive,
+        name = "com_github_grpc_grpc_web",
+        sha256 = "04460e28ffa80bfc797a8758da10ba40107347ef0af8e9cc065ade10398da4bb",
+        strip_prefix = "grpc-web-1.0.7",
+        urls = [
+            "https://github.com/grpc/grpc-web/archive/1.0.7.tar.gz",
+        ],
+    )
+
+    _maybe(
+        http_archive,
+        name = "rules_proto",
+        sha256 = "4d421d51f9ecfe9bf96ab23b55c6f2b809cbaf0eea24952683e397decfbd0dd0",
+        strip_prefix = "rules_proto-f6b8d89b90a7956f6782a4a3609b2f0eee3ce965",
+        urls = [
+            "https://github.com/bazelbuild/rules_proto/archive/f6b8d89b90a7956f6782a4a3609b2f0eee3ce965.tar.gz",
+        ],
+    )
+
+    _maybe(
+        mock_io_bazel_rules_closure,
+        name = "io_bazel_rules_closure",
+    )
+
+    yarn_install(
+        name = "build_bazel_rules_typescript_grpc_web_compiletime_deps",
+        package_json = "@npm_bazel_labs//grpc_web:package.json",
+        yarn_lock = "@npm_bazel_labs//grpc_web:yarn.lock",
+        # Do not symlink node_modules as when used in downstream repos we should not create
+        # node_modules folders in the @npm_bazel_typescript external repository. This is
+        # not supported by managed_directories.
+        symlink_node_modules = False,
+    )
+
     yarn_install(
         name = "build_bazel_rules_typescript_protobufs_compiletime_deps",
         package_json = "@npm_bazel_labs//protobufjs:package.json",
@@ -12,3 +66,7 @@
         # not supported by managed_directories.
         symlink_node_modules = False,
     )
+
+def _maybe(repo_rule, name, **kwargs):
+    if name not in native.existing_rules():
+        repo_rule(name = name, **kwargs)
diff --git a/packages/labs/src/tsconfig.json b/packages/labs/src/tsconfig.json
deleted file mode 100644
index 8dc1429..0000000
--- a/packages/labs/src/tsconfig.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-    "compilerOptions": {
-        "strict": true
-    }
-}
diff --git a/packages/labs/test/grpc_web/BUILD.bazel b/packages/labs/test/grpc_web/BUILD.bazel
new file mode 100644
index 0000000..204bca2
--- /dev/null
+++ b/packages/labs/test/grpc_web/BUILD.bazel
@@ -0,0 +1,116 @@
+load("@npm_bazel_jasmine//:index.from_src.bzl", "jasmine_node_test")
+load("@npm_bazel_karma//:index.from_src.bzl", "karma_web_test_suite")
+load("@npm_bazel_rollup//:index.from_src.bzl", "rollup_bundle")
+load("@npm_bazel_typescript//:index.from_src.bzl", "ts_library")
+
+# This test checks that the protos can be resolved in a nodejs environment
+jasmine_node_test(
+    name = "commonjs_test",
+    data = [
+        "@npm//google-protobuf",
+        "@npm//grpc-web",
+    ],
+    deps = [
+        ":commonjs_test_lib",
+    ],
+)
+
+ts_library(
+    name = "commonjs_test_lib",
+    srcs = [
+        "commonjs_test.spec.ts",
+    ],
+    deps = [
+        "//packages/labs/test/grpc_web/proto:pizza_service_ts_proto",
+        "//packages/labs/test/grpc_web/proto/common:delivery_person_ts_proto",
+        "@npm//@types/jasmine",
+    ],
+)
+
+ts_library(
+    name = "proto_with_deps_test",
+    testonly = 1,
+    srcs = ["proto_with_deps_test.spec.ts"],
+    deps = [
+        "//packages/labs/test/grpc_web/proto/common:delivery_person_ts_proto",
+        "@npm//@types/jasmine",
+        # Don't put pizza_ts_proto here, we want to test it is included transitively
+    ],
+)
+
+karma_web_test_suite(
+    name = "proto_with_deps_test_suite",
+    srcs = [
+        "@npm_bazel_labs//grpc_web:bootstrap_scripts",
+    ],
+    browsers = [
+        "@io_bazel_rules_webtesting//browsers:chromium-local",
+    ],
+    tags = ["native"],
+    deps = [
+        ":proto_with_deps_test",
+    ],
+)
+
+ts_library(
+    name = "pizza_service_proto_test",
+    testonly = 1,
+    srcs = ["pizza_service_proto_test.spec.ts"],
+    deps = [
+        "//packages/labs/test/grpc_web/proto/common:pizza_ts_proto",
+        "@npm//@types/jasmine",
+        "//packages/labs/test/grpc_web/proto:pizza_service_ts_proto",
+        # Don't put delivery_person_ts_proto here, we want to test it is included transitively
+    ],
+)
+
+karma_web_test_suite(
+    name = "pizza_service_proto_test_suite",
+    srcs = [
+        "@npm_bazel_labs//grpc_web:bootstrap_scripts",
+    ],
+    browsers = [
+        "@io_bazel_rules_webtesting//browsers:chromium-local",
+    ],
+    tags = ["native"],
+    deps = [
+        ":pizza_service_proto_test",
+    ],
+)
+
+rollup_bundle(
+    name = "test_es6_bundling",
+    config_file = "rollup.config.js",
+    entry_points = {
+        ":test_bundling.ts": "index",
+    },
+    format = "cjs",
+    output_dir = True,
+    deps = [
+        ":test_bundling_lib",
+        "@npm//rollup-plugin-commonjs",
+        "@npm//rollup-plugin-node-resolve",
+    ],
+)
+
+ts_library(
+    name = "test_bundling_lib",
+    srcs = ["test_bundling.ts"],
+    deps = [
+        "//packages/labs/test/grpc_web/proto:naming_styles_ts_proto",
+        "//packages/labs/test/grpc_web/proto/common:delivery_person_ts_proto",
+        "//packages/labs/test/grpc_web/proto/common:pizza_ts_proto",
+        "@npm//google-protobuf",
+        "@npm//grpc-web",
+    ],
+)
+
+jasmine_node_test(
+    name = "rollup_test",
+    srcs = [
+        ":rollup_test.spec.js",
+    ],
+    data = [
+        ":test_es6_bundling",
+    ],
+)
diff --git a/packages/labs/test/grpc_web/commonjs_test.spec.ts b/packages/labs/test/grpc_web/commonjs_test.spec.ts
new file mode 100644
index 0000000..e49d23e
--- /dev/null
+++ b/packages/labs/test/grpc_web/commonjs_test.spec.ts
@@ -0,0 +1,16 @@
+import deliveryPersonPb = require('build_bazel_rules_nodejs/packages/labs/test/grpc_web/proto/common/delivery_person_pb');
+import {PizzaServiceClient} from 'build_bazel_rules_nodejs/packages/labs/test/grpc_web/proto/pizza_service_grpc_web_pb';
+
+describe('CommonJs', () => {
+  it('Loads imports using require()', () => {
+    expect(deliveryPersonPb).toBeDefined();
+
+    const person = new deliveryPersonPb.DeliveryPerson();
+    person.setName('Doug');
+    expect(person).toBeDefined();
+  });
+
+  it('Loads imports using TS from syntax', () => {
+    expect(PizzaServiceClient).toBeDefined();
+  });
+});
diff --git a/packages/labs/test/grpc_web/pizza_service_proto_test.spec.ts b/packages/labs/test/grpc_web/pizza_service_proto_test.spec.ts
new file mode 100644
index 0000000..9fa649a
--- /dev/null
+++ b/packages/labs/test/grpc_web/pizza_service_proto_test.spec.ts
@@ -0,0 +1,41 @@
+import 'google-protobuf';
+
+import {Pizza, PizzaSize} from 'build_bazel_rules_nodejs/packages/labs/test/grpc_web/proto/common/pizza_pb';
+import {PizzaServiceClient} from 'build_bazel_rules_nodejs/packages/labs/test/grpc_web/proto/pizza_service_grpc_web_pb';
+import {OrderPizzaRequest, OrderPizzaResponse} from 'build_bazel_rules_nodejs/packages/labs/test/grpc_web/proto/pizza_service_pb';
+
+declare function require(module: string): any;
+
+describe('DeliveryPerson', () => {
+  it('Non-service imported classes should not be null', () => {
+    expect(OrderPizzaRequest).toBeDefined();
+    expect(OrderPizzaResponse).toBeDefined();
+  });
+
+  it('Service imported class should not be null', () => {
+    expect(PizzaServiceClient).toBeDefined();
+  });
+
+  it('Generated code seems to work', () => {
+    const request = new OrderPizzaRequest();
+
+    const pizza = new Pizza();
+    pizza.setSize(PizzaSize.PIZZA_SIZE_LARGE);
+    request.addPizzas(pizza);
+
+    expect(request.getPizzasList().length).toBe(1);
+  });
+
+  it('delivery_person_ts_proto is included since it is a transitive dependency', () => {
+    const PROTOS = require(
+        'build_bazel_rules_nodejs/packages/labs/test/grpc_web/proto/common/delivery_person_pb');
+    const DeliveryPerson = PROTOS.DeliveryPerson;
+    const pizza = new Pizza();
+    pizza.setSize(PizzaSize.PIZZA_SIZE_LARGE);
+
+    const person = new DeliveryPerson();
+    person.setPizzasList([pizza]);
+
+    expect(person.getPizzasList().length).toBe(1);
+  });
+});
diff --git a/packages/labs/test/grpc_web/proto/BUILD.bazel b/packages/labs/test/grpc_web/proto/BUILD.bazel
new file mode 100644
index 0000000..ed53f63
--- /dev/null
+++ b/packages/labs/test/grpc_web/proto/BUILD.bazel
@@ -0,0 +1,33 @@
+load("@rules_proto//proto:defs.bzl", "proto_library")
+
+package(default_visibility = ["//:__subpackages__"])
+
+load("@npm_bazel_labs//:index.bzl", "ts_proto_library")
+
+proto_library(
+    name = "naming_styles_proto",
+    srcs = [
+        "naming_styles.proto",
+    ],
+)
+
+ts_proto_library(
+    name = "naming_styles_ts_proto",
+    proto = ":naming_styles_proto",
+)
+
+proto_library(
+    name = "pizza_service_proto",
+    srcs = [
+        "pizza_service.proto",
+    ],
+    deps = [
+        "//packages/labs/test/grpc_web/proto/common:delivery_person_proto",
+        "//packages/labs/test/grpc_web/proto/common:pizza_proto",
+    ],
+)
+
+ts_proto_library(
+    name = "pizza_service_ts_proto",
+    proto = ":pizza_service_proto",
+)
diff --git a/packages/labs/test/grpc_web/proto/common/BUILD.bazel b/packages/labs/test/grpc_web/proto/common/BUILD.bazel
new file mode 100644
index 0000000..cc4ac0d
--- /dev/null
+++ b/packages/labs/test/grpc_web/proto/common/BUILD.bazel
@@ -0,0 +1,32 @@
+load("@rules_proto//proto:defs.bzl", "proto_library")
+
+package(default_visibility = ["//:__subpackages__"])
+
+load("@npm_bazel_labs//:index.bzl", "ts_proto_library")
+
+proto_library(
+    name = "delivery_person_proto",
+    srcs = [
+        "delivery_person.proto",
+    ],
+    deps = [
+        ":pizza_proto",
+    ],
+)
+
+ts_proto_library(
+    name = "delivery_person_ts_proto",
+    proto = ":delivery_person_proto",
+)
+
+proto_library(
+    name = "pizza_proto",
+    srcs = [
+        "pizza.proto",
+    ],
+)
+
+ts_proto_library(
+    name = "pizza_ts_proto",
+    proto = ":pizza_proto",
+)
diff --git a/packages/labs/test/grpc_web/proto/common/delivery_person.proto b/packages/labs/test/grpc_web/proto/common/delivery_person.proto
new file mode 100644
index 0000000..667655c
--- /dev/null
+++ b/packages/labs/test/grpc_web/proto/common/delivery_person.proto
@@ -0,0 +1,19 @@
+syntax = "proto3";
+
+package test.bazel.proto;
+
+import "packages/labs/test/grpc_web/proto/common/pizza.proto";
+
+message DeliveryPerson {
+  // The name of the delivery person.
+  string name = 1;
+
+  // The list of pizzas the delivery person has left to deliver.
+  repeated Pizza pizzas = 2;
+
+  // Test nested structures inside protobufs.
+  oneof data {
+    string id = 3;
+    int64 id_v2 = 4;
+  }
+}
diff --git a/packages/labs/test/grpc_web/proto/common/pizza.proto b/packages/labs/test/grpc_web/proto/common/pizza.proto
new file mode 100644
index 0000000..48cf4cc
--- /dev/null
+++ b/packages/labs/test/grpc_web/proto/common/pizza.proto
@@ -0,0 +1,18 @@
+syntax = "proto3";
+
+package test.bazel.proto;
+
+enum PizzaSize {
+  PIZZA_SIZE_UNKNOWN = 0;
+  PIZZA_SIZE_SMALL = 1;
+  PIZZA_SIZE_MEDIUM = 2;
+  PIZZA_SIZE_LARGE = 3;
+}
+
+message Pizza {
+  // The size of the pizza.
+  PizzaSize size = 1;
+
+  // The list of toppings that are on the pizza.
+  repeated string topping_ids = 2;
+}
diff --git a/packages/labs/test/grpc_web/proto/naming_styles.proto b/packages/labs/test/grpc_web/proto/naming_styles.proto
new file mode 100644
index 0000000..3cb6948
--- /dev/null
+++ b/packages/labs/test/grpc_web/proto/naming_styles.proto
@@ -0,0 +1,60 @@
+// This file tests a bunch of valid naming schemes for messages.
+syntax = "proto3";
+
+package test.bazel.proto;
+
+message alllowercase {
+  int64 test = 1;
+}
+
+message ALLUPPERCASE {
+  int64 test = 1;
+}
+
+message lowerCamelCase {
+  int64 test = 1;
+}
+
+message UpperCamelCase {
+  int64 test = 1;
+}
+
+message snake_case_snake_case {
+  int64 test = 1;
+}
+
+message Upper_snake_Case {
+  int64 test = 1;
+}
+
+message M2M {
+  int64 test = 1;
+}
+
+message M_2M {
+  int64 test = 1;
+}
+
+message M2_M {
+  int64 test = 1;
+}
+
+message M2M_ {
+  int64 test = 1;
+}
+
+message m_22M {
+  int64 test = 1;
+}
+
+message m42_M {
+  int64 test = 1;
+}
+
+message m24M_ {
+  int64 test = 1;
+}
+
+message M9 {
+  int64 test = 1;
+}
diff --git a/packages/labs/test/grpc_web/proto/pizza_service.proto b/packages/labs/test/grpc_web/proto/pizza_service.proto
new file mode 100644
index 0000000..0f5d684
--- /dev/null
+++ b/packages/labs/test/grpc_web/proto/pizza_service.proto
@@ -0,0 +1,21 @@
+syntax = "proto3";
+
+package test.bazel.proto;
+
+import "packages/labs/test/grpc_web/proto/common/pizza.proto";
+import "packages/labs/test/grpc_web/proto/common/delivery_person.proto";
+
+service PizzaService {
+  rpc OrderPizza(OrderPizzaRequest) returns (OrderPizzaResponse) {
+  }
+}
+
+message OrderPizzaRequest {
+  // The list of pizzas to order.
+  repeated Pizza pizzas = 1;
+}
+
+message OrderPizzaResponse {
+  // The person that will deliver the pizza.
+  DeliveryPerson delivery_person = 1;
+}
diff --git a/packages/labs/test/grpc_web/proto_with_deps_test.spec.ts b/packages/labs/test/grpc_web/proto_with_deps_test.spec.ts
new file mode 100644
index 0000000..cadf7f2
--- /dev/null
+++ b/packages/labs/test/grpc_web/proto_with_deps_test.spec.ts
@@ -0,0 +1,32 @@
+import {DeliveryPerson} from 'build_bazel_rules_nodejs/packages/labs/test/grpc_web/proto/common/delivery_person_pb';
+
+declare function require(module: string): any;
+
+describe('DeliveryPerson', () => {
+  it('Imported class should not be null', () => {
+    expect(DeliveryPerson).toBeDefined();
+  });
+
+  it('Generated code seems to work', () => {
+    const person = new DeliveryPerson();
+    const name = 'Doug';
+
+    person.setName(name);
+
+    expect(person.getName()).toBe(name);
+  });
+
+  it('pizza_ts_proto is included since it is a transitive dependency', () => {
+    const PROTOS =
+        require('build_bazel_rules_nodejs/packages/labs/test/grpc_web/proto/common/pizza_pb');
+    const Pizza = PROTOS.Pizza;
+    const PizzaSize = PROTOS.PizzaSize;
+    const person = new DeliveryPerson();
+
+    const pizza = new Pizza();
+    pizza.setSize(PizzaSize.PIZZA_SIZE_LARGE);
+    person.setPizzasList([pizza]);
+
+    expect(person.getPizzasList().length).toBe(1);
+  });
+});
diff --git a/packages/labs/test/grpc_web/rollup.config.js b/packages/labs/test/grpc_web/rollup.config.js
new file mode 100644
index 0000000..9108670
--- /dev/null
+++ b/packages/labs/test/grpc_web/rollup.config.js
@@ -0,0 +1,9 @@
+const commonjs = require('rollup-plugin-commonjs');
+const nodeRequire = require('rollup-plugin-node-resolve');
+
+module.exports = {
+  plugins: [
+    nodeRequire(),
+    commonjs(),
+  ],
+};
diff --git a/packages/labs/test/grpc_web/rollup_test.spec.js b/packages/labs/test/grpc_web/rollup_test.spec.js
new file mode 100644
index 0000000..ae0bf36
--- /dev/null
+++ b/packages/labs/test/grpc_web/rollup_test.spec.js
@@ -0,0 +1,22 @@
+const grpcWeb = require('grpc-web');
+grpcWeb.MethodType = {
+  UNARY: 'unary'
+};
+const bundle = require('build_bazel_rules_nodejs/packages/labs/test/grpc_web/test_es6_bundling/');
+
+describe('Rollup', () => {
+  it('should define Pizza with protobuf API', () => {
+    expect(bundle.Pizza).toBeDefined();
+
+    const pizza = new bundle.Pizza();
+    pizza.setSize(bundle.PizzaSize.PIZZA_SIZE_LARGE);
+
+    expect(pizza.getSize()).toBe(bundle.PizzaSize.PIZZA_SIZE_LARGE);
+    expect(Array.isArray(pizza.getToppingIdsList())).toBe(true);
+  });
+
+  it('should define DeliveryPerson', () => {
+    expect(bundle.DeliveryPerson).toBeDefined();
+    expect(new bundle.DeliveryPerson()).toBeTruthy();
+  });
+});
diff --git a/packages/labs/test/grpc_web/test_bundling.ts b/packages/labs/test/grpc_web/test_bundling.ts
new file mode 100644
index 0000000..f8cb156
--- /dev/null
+++ b/packages/labs/test/grpc_web/test_bundling.ts
@@ -0,0 +1,2 @@
+export {DeliveryPerson} from 'build_bazel_rules_nodejs/packages/labs/test/grpc_web/proto/common/delivery_person_pb';
+export {Pizza, PizzaSize} from 'build_bazel_rules_nodejs/packages/labs/test/grpc_web/proto/common/pizza_pb';
diff --git a/packages/labs/test/protobufjs/BUILD.bazel b/packages/labs/test/protobufjs/BUILD.bazel
index 49a20c1..45485ca 100644
--- a/packages/labs/test/protobufjs/BUILD.bazel
+++ b/packages/labs/test/protobufjs/BUILD.bazel
@@ -1,5 +1,5 @@
 load("@npm_bazel_jasmine//:index.from_src.bzl", "jasmine_node_test")
-load("@npm_bazel_labs//:index.bzl", "ts_proto_library")
+load("@npm_bazel_labs//:index.bzl", "protobufjs_ts_library")
 load("@npm_bazel_typescript//:index.from_src.bzl", "ts_library")
 load("@rules_proto//proto:defs.bzl", "proto_library")
 
@@ -17,7 +17,7 @@
     srcs = [":test.proto"],
 )
 
-ts_proto_library(
+protobufjs_ts_library(
     name = "test_ts_proto",
     deps = [":test_proto"],
 )
diff --git a/tsconfig.json b/tsconfig.json
index 4484aa0..0d66552 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,6 +2,7 @@
     "compilerOptions": {
         "lib": ["es2017", "dom"],
         "strict": true,
-        "target": "es2015"
+        "baseUrl": ".",
+        "target": "es2015",
     }
 }
diff --git a/yarn.lock b/yarn.lock
index 8676ca6..8f4a93e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3482,6 +3482,11 @@
     pify "^2.0.0"
     pinkie-promise "^2.0.0"
 
+google-protobuf@^3.6.1:
+  version "3.11.4"
+  resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.11.4.tgz#598ca405a3cfa917a2132994d008b5932ef42014"
+  integrity sha512-lL6b04rDirurUBOgsY2+LalI6Evq8eH5TcNzi7TYQ3BsIWelT0KSOQSBsXuavEkNf+odQU6c0lgz3UsZXeNX9Q==
+
 got@^6.7.1:
   version "6.7.1"
   resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
@@ -3509,6 +3514,11 @@
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
   integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
 
+grpc-web@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/grpc-web/-/grpc-web-1.0.7.tgz#9e4fbcf63d3734515332ab59e42baa7d0d290015"
+  integrity sha512-Fkbz1nyvvt6GC6ODcxh9Fen6LLB3OTCgGHzHwM2Eni44SUhzqPz1UQgFp9sfBEfInOhx3yBdwo9ZLjZAmJ+TtA==
+
 handlebars@^4.0.3, handlebars@^4.1.2:
   version "4.5.3"
   resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482"