Define global.gc using v8 and vm modules if needed

This allows us to tear down a bunch of complexity related to setting the --expose-gc flag when starting nodejs

Closes #415

PiperOrigin-RevId: 233451681
diff --git a/BUILD.bazel b/BUILD.bazel
index 09ee44b..0551730 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -25,7 +25,7 @@
 load("@bazel_gazelle//:def.bzl", "gazelle")
 
 # END-DEV-ONLY
-load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary", "npm_package")
+load("@build_bazel_rules_nodejs//:defs.bzl", "npm_package")
 
 # BEGIN-DEV-ONLY
 load("@build_bazel_rules_nodejs//internal/js_library:js_library.bzl", "js_library")
@@ -108,18 +108,3 @@
 # )
 
 # END-DEV-ONLY
-# A nodejs_binary for @bazel/typescript/tsc_wrapped to use by default in
-# ts_library that depends on @npm//@bazel/typescript instead of the
-# output of the //internal/tsc_wrapped ts_library rule. This
-# default is for downstream users that depend on the @bazel/typescript npm
-# package. A generated @npm//@bazel/typescript/bin:tsc_wrapped target
-# would not work because it does not have the node `--expose-gc` flag
-# set which is required to support the call to `global.gc()`.
-nodejs_binary(
-    name = "@bazel/typescript/tsc_wrapped",
-    data = ["@npm//@bazel/typescript"],
-    entry_point = "@bazel/typescript/internal/tsc_wrapped/tsc_wrapped.js",
-    install_source_map_support = False,
-    templated_args = ["--node_options=--expose-gc"],
-    visibility = ["//visibility:public"],
-)
diff --git a/README.md b/README.md
index c8082c3..074475b 100644
--- a/README.md
+++ b/README.md
@@ -140,8 +140,6 @@
 nodejs_binary(
     name = "@bazel/typescript/tsc_wrapped",
     entry_point = "@bazel/typescript/internal/tsc_wrapped/tsc_wrapped.js",
-    # The --expose-gc node option is required for tsc_wrapped
-    templated_args = ["--node_options=--expose-gc"],
     # Point bazel to your node_modules to find the entry point
     node_modules = ["//:node_modules"],
 )
diff --git a/internal/BUILD.bazel b/internal/BUILD.bazel
index 5132aa8..6417455 100644
--- a/internal/BUILD.bazel
+++ b/internal/BUILD.bazel
@@ -86,7 +86,6 @@
         "@npm//typescript",
     ],
     entry_point = "build_bazel_rules_typescript/internal/tsc_wrapped/tsc_wrapped.js",
-    templated_args = ["--node_options=--expose-gc"],
     visibility = ["//visibility:public"],
 )
 
diff --git a/internal/build_defs.bzl b/internal/build_defs.bzl
index fb22d2a..1639546 100644
--- a/internal/build_defs.bzl
+++ b/internal/build_defs.bzl
@@ -22,7 +22,7 @@
 load(":common/tsconfig.bzl", "create_tsconfig")
 load(":ts_config.bzl", "TsConfigInfo")
 
-_DEFAULT_COMPILER = "@build_bazel_rules_typescript//:@bazel/typescript/tsc_wrapped"
+_DEFAULT_COMPILER = "@npm//@bazel/typescript/bin:tsc_wrapped"
 
 def _trim_package_node_modules(package_name):
     # trim a package name down to its path prior to a node_modules
diff --git a/internal/e2e/default_tsconfig_test.js b/internal/e2e/default_tsconfig_test.js
index 7711521..a71affd 100644
--- a/internal/e2e/default_tsconfig_test.js
+++ b/internal/e2e/default_tsconfig_test.js
@@ -387,7 +387,7 @@
 ${WORKSPACE_BOILERPLATE}`);
        write('a/BUILD', `
 # We use ts_library from internal/defaults.bzl since we don't have a @bazel/typescript npm
-# package in this test. This changes the ts_library compiler from the default '@build_bazel_rules_typescript//:@bazel/typescript/tsc_wrapped'
+# package in this test. This changes the ts_library compiler from the default
 # which depends on @npm//@bazel/typescript which is not available in this test to '@build_bazel_rules_typescript//internal:tsc_wrapped_bin' which is
 load("@build_bazel_rules_typescript//internal:defaults.bzl", "ts_library")
 ts_library(
@@ -410,7 +410,7 @@
 ${WORKSPACE_BOILERPLATE}`);
        write('b/BUILD', `
 # We use ts_library from internal/defaults.bzl since we don't have a @bazel/typescript npm
-# package in this test. This changes the ts_library compiler from the default '@build_bazel_rules_typescript//:@bazel/typescript/tsc_wrapped'
+# package in this test. This changes the ts_library compiler from the default
 # which depends on @npm//@bazel/typescript which is not available in this test to '@build_bazel_rules_typescript//internal:tsc_wrapped_bin' which is
 load("@build_bazel_rules_typescript//internal:defaults.bzl", "ts_library")
 exports_files(["tsconfig.json"])
diff --git a/internal/tsc_wrapped/tsc_wrapped.ts b/internal/tsc_wrapped/tsc_wrapped.ts
index 9aa7473..2062b73 100644
--- a/internal/tsc_wrapped/tsc_wrapped.ts
+++ b/internal/tsc_wrapped/tsc_wrapped.ts
@@ -15,6 +15,14 @@
 import {BazelOptions, parseTsconfig, resolveNormalizedPath} from './tsconfig';
 import {debug, log, runAsWorker, runWorkerLoop} from './worker';
 
+// Equivalent of running node with --expose-gc
+// but easier to write tooling since we don't need to inject that arg to
+// nodejs_binary
+if (typeof global.gc !== 'function') {
+  require('v8').setFlagsFromString('--expose_gc');
+  global.gc = require('vm').runInNewContext('gc');
+}
+
 /**
  * Top-level entry point for tsc_wrapped.
  */
diff --git a/internal/tsc_wrapped/worker.ts b/internal/tsc_wrapped/worker.ts
index 4a102cc..7e52ba8 100644
--- a/internal/tsc_wrapped/worker.ts
+++ b/internal/tsc_wrapped/worker.ts
@@ -4,6 +4,14 @@
 // tslint:disable-next-line:variable-name: ByteBuffer is instantiatable.
 const ByteBuffer = require('bytebuffer');
 
+// Equivalent of running node with --expose-gc
+// but easier to write tooling since we don't need to inject that arg to
+// nodejs_binary
+if (typeof global.gc !== 'function') {
+  require('v8').setFlagsFromString('--expose_gc');
+  global.gc = require('vm').runInNewContext('gc');
+}
+
 export const DEBUG = false;
 
 export function debug(...args: Array<{}>) {
diff --git a/package.json b/package.json
index 9908625..8b9df61 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,8 @@
     "main": "./internal/tsc_wrapped/index.js",
     "typings": "./internal/tsc_wrapped/index.d.ts",
     "bin": {
-        "ts_auto_deps": "./ts_auto_deps/ts_auto_deps.js"
+        "ts_auto_deps": "./ts_auto_deps/ts_auto_deps.js",
+        "tsc_wrapped": "./internal/tsc_wrapped/tsc_wrapped.js"
     },
     "dependencies": {
         "protobufjs": "5.0.3",