From 5cd8bf53a6ef3855e2a5d26c2b66e2127a87b873 Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Thu, 2 Nov 2023 22:49:03 -0700 Subject: [PATCH] use swc instead of babel for faster bazel typescript transpilation (#57912) swc is a very fast TypeScript transpiler. Instead of using Babel for TypeScript transpilation in Bazel, we now use swc, which is much faster. We still use Babel in Jest (for tests), but in the future we intend to move away from Jest. See https://docs.aspect.build/rulesets/aspect_rules_ts/docs/transpiler#swc-recommended for more information about using swc in Bazel, and https://swc.rs/ for general information about swc. --- .swcrc | 10 ++ BUILD.bazel | 1 + WORKSPACE | 29 +++++- client/browser/BUILD.bazel | 2 +- client/browser/config/BUILD.bazel | 34 ++++++- .../types/webextension-polyfill/index.d.ts | 4 - client/build-config/BUILD.bazel | 1 - client/shared/src/testing/BUILD.bazel | 1 - .../testing/integration/polly/CdpAdapter.ts | 5 +- client/web/BUILD.bazel | 7 +- client/web/dev/BUILD.bazel | 35 ++++++- client/web/scripts/BUILD.bazel | 1 - client/web/src/end-to-end/BUILD.bazel | 2 - client/web/src/integration/BUILD.bazel | 2 - dev/babel.bzl | 95 ------------------- dev/defs.bzl | 27 +++--- dev/mocha.bzl | 4 - 17 files changed, 123 insertions(+), 137 deletions(-) create mode 100644 .swcrc delete mode 100644 dev/babel.bzl diff --git a/.swcrc b/.swcrc new file mode 100644 index 00000000000..7f1317d0f30 --- /dev/null +++ b/.swcrc @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "jsc": { + "transform": { + "react": { "runtime": "automatic" } + }, + "parser": { "syntax": "typescript" }, + "target": "es2022" + } +} diff --git a/BUILD.bazel b/BUILD.bazel index 8caaa6d84f3..9c22ead4c4e 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -337,4 +337,5 @@ exports_files([ # Used for when copy_to_directory might reference an empty filegroup # under certain conditions. See //ui/assets/... "CONTRIBUTING.md", + ".swcrc", ]) diff --git a/WORKSPACE b/WORKSPACE index ae70f527866..4f91499a1b2 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -49,6 +49,13 @@ http_archive( url = "https://github.com/aspect-build/rules_jest/archive/95d8f1961a9c6f3aee2929881b1b74461652e775.tar.gz", ) +http_archive( + name = "aspect_rules_swc", + sha256 = "8eb9e42ed166f20cacedfdb22d8d5b31156352eac190fc3347db55603745a2d8", + strip_prefix = "rules_swc-1.1.0", + url = "https://github.com/aspect-build/rules_swc/releases/download/v1.1.0/rules_swc-v1.1.0.tar.gz", +) + http_archive( name = "io_bazel_rules_go", sha256 = "51dc53293afe317d2696d4d6433a4c33feedb7748a9e352072e2ec3c0dafd2c6", @@ -206,12 +213,24 @@ load("@jest//:npm_repositories.bzl", jest_npm_repositories = "npm_repositories") jest_npm_repositories() +# rules_swc setup ============================== +load("@aspect_rules_swc//swc:dependencies.bzl", "rules_swc_dependencies") + +rules_swc_dependencies() + +load("@aspect_rules_swc//swc:repositories.bzl", "LATEST_SWC_VERSION", "swc_register_toolchains") + +swc_register_toolchains( + name = "swc", + swc_version = LATEST_SWC_VERSION, +) + # rules_esbuild setup =========================== http_archive( name = "aspect_rules_esbuild", - sha256 = "2ea31bd97181a315e048be693ddc2815fddda0f3a12ca7b7cc6e91e80f31bac7", - strip_prefix = "rules_esbuild-0.14.4", - url = "https://github.com/aspect-build/rules_esbuild/releases/download/v0.14.4/rules_esbuild-v0.14.4.tar.gz", + sha256 = "84419868e43c714c0d909dca73039e2f25427fc04f352d2f4f7343ca33f60deb", + strip_prefix = "rules_esbuild-0.15.3", + url = "https://github.com/aspect-build/rules_esbuild/releases/download/v0.15.3/rules_esbuild-v0.15.3.tar.gz", ) load("@aspect_rules_esbuild//esbuild:dependencies.bzl", "rules_esbuild_dependencies") @@ -219,11 +238,11 @@ load("@aspect_rules_esbuild//esbuild:dependencies.bzl", "rules_esbuild_dependenc rules_esbuild_dependencies() # Register a toolchain containing esbuild npm package and native bindings -load("@aspect_rules_esbuild//esbuild:repositories.bzl", "LATEST_VERSION", "esbuild_register_toolchains") +load("@aspect_rules_esbuild//esbuild:repositories.bzl", "LATEST_ESBUILD_VERSION", "esbuild_register_toolchains") esbuild_register_toolchains( name = "esbuild", - esbuild_version = LATEST_VERSION, + esbuild_version = LATEST_ESBUILD_VERSION, ) # Go toolchain setup diff --git a/client/browser/BUILD.bazel b/client/browser/BUILD.bazel index e85b75ac981..637b62ed490 100644 --- a/client/browser/BUILD.bazel +++ b/client/browser/BUILD.bazel @@ -381,7 +381,7 @@ esbuild( # STATIC ASSETS "//client/browser/assets", ], - config = "//client/browser/config:esbuild-config", + config = "//client/browser/config:esbuild-config_bundle", entry_points = [ "src/browser-extension/scripts/backgroundPage.main.js", "src/browser-extension/scripts/contentPage.main.js", diff --git a/client/browser/config/BUILD.bazel b/client/browser/config/BUILD.bazel index 3c170ae9cd2..51c2117ebec 100644 --- a/client/browser/config/BUILD.bazel +++ b/client/browser/config/BUILD.bazel @@ -1,4 +1,5 @@ load("//dev:defs.bzl", "ts_project") +load("@aspect_rules_esbuild//esbuild:defs.bzl", "esbuild") load("@aspect_rules_js//js:defs.bzl", "js_library") # config/ does not contain a src/ @@ -33,10 +34,41 @@ ts_project( js_library( name = "esbuild-config", srcs = ["esbuild.bazel.js"], - visibility = ["//client:__subpackages__"], deps = [ "//client/browser:node_modules/@sourcegraph/build-config", "//client/browser/config", "//client/build-config:build-config_lib", ], ) + +esbuild( + name = "esbuild-config_bundle", + srcs = [ + ":esbuild-config", + ], + entry_point = "esbuild.bazel.js", + external = [ + "fsevents", + "pnpapi", + "../../../../postcss.config", + + # suppress esbuild require-resolve-not-external warnings + "esbuild", + "path-browserify", + "monaco-yaml/lib/esm/monaco.contribution", + "monaco-yaml/lib/esm/yaml.worker", + "rxjs/_esm5/internal/OuterSubscriber", + "rxjs/_esm5/internal/util/subscribeToResult", + "rxjs/_esm5/internal/util/subscribeToArray", + "rxjs/_esm5/internal/Observable", + ], + format = "cjs", + platform = "node", + sourcemap = False, + splitting = False, + target = "es2022", + visibility = ["//client:__subpackages__"], + deps = [ + "//:postcss_config_js", + ], +) diff --git a/client/browser/src/types/webextension-polyfill/index.d.ts b/client/browser/src/types/webextension-polyfill/index.d.ts index 49377d42c00..7400dc5eed8 100644 --- a/client/browser/src/types/webextension-polyfill/index.d.ts +++ b/client/browser/src/types/webextension-polyfill/index.d.ts @@ -430,10 +430,6 @@ declare namespace browser.contentScripts { declare namespace browser.devtools.inspectedWindow { const tabId: number - function eval( - expression: string - ): Promise<[any, { isException: boolean; value: string } | { isError: boolean; code: string }]> - function reload(reloadOptions?: { ignoreCache?: boolean; userAgent?: string; injectedScript?: string }): void } diff --git a/client/build-config/BUILD.bazel b/client/build-config/BUILD.bazel index 16ae49847b4..19d2ea9b45d 100644 --- a/client/build-config/BUILD.bazel +++ b/client/build-config/BUILD.bazel @@ -35,7 +35,6 @@ ts_project( data = [ "//:postcss_config_js", #keep ], - module = "commonjs", tsconfig = ":tsconfig", deps = [ ":node_modules/@types/sass", diff --git a/client/shared/src/testing/BUILD.bazel b/client/shared/src/testing/BUILD.bazel index 34ab4456017..f1eca14ea99 100644 --- a/client/shared/src/testing/BUILD.bazel +++ b/client/shared/src/testing/BUILD.bazel @@ -57,7 +57,6 @@ ts_project( "utils.ts", ], tsconfig = "//client/shared:tsconfig", - use_preset_env = False, deps = [ "//:node_modules/@apollo/client", "//:node_modules/@axe-core/puppeteer", diff --git a/client/shared/src/testing/integration/polly/CdpAdapter.ts b/client/shared/src/testing/integration/polly/CdpAdapter.ts index 2e5658807aa..ea9568740fc 100644 --- a/client/shared/src/testing/integration/polly/CdpAdapter.ts +++ b/client/shared/src/testing/integration/polly/CdpAdapter.ts @@ -58,8 +58,11 @@ export class CdpAdapter extends PollyAdapter { /** * `adapterOptions` passed to Polly. + * + * Uses `declare` because otherwise esbuild overwrites the superclass PollyAdapter's `options` + * field. See https://github.com/evanw/esbuild/issues/885. */ - public options!: CdpAdapterOptions + public declare options: CdpAdapterOptions private readonly _errors = new Subject() diff --git a/client/web/BUILD.bazel b/client/web/BUILD.bazel index 104ced4304b..5a1547625a1 100644 --- a/client/web/BUILD.bazel +++ b/client/web/BUILD.bazel @@ -2120,7 +2120,7 @@ BUNDLE_DATA_DEPS = [ # TODO(bazel): this is already in the main ts_project(srcs). Why also here? "src/enterprise/codeintel/configuration/schema.json", ":enterprise-yaml", - "//client/web/dev:esbuild-config-production", + "//client/web/dev:esbuild-config-production_bundle", ] esbuild( @@ -2129,7 +2129,7 @@ esbuild( ":web_lib", "//:package_json", ], - config = "//client/web/dev:esbuild-config-production", + config = "//client/web/dev:esbuild-config-production_bundle", entry_points = [ "src/enterprise/main.js", "src/enterprise/embed/embedMain.js", @@ -2148,7 +2148,7 @@ esbuild_web_app( ":web_lib", "//:package_json", ], - config = "//client/web/dev:esbuild-config-production", + config = "//client/web/dev:esbuild-config-production_bundle", define = { "process.env.INTEGRATION_TESTS": "true", }, @@ -2156,6 +2156,7 @@ esbuild_web_app( "src/enterprise/main.js", "src/enterprise/embed/embedMain.js", ], + sourcemap = "linked", visibility = ["//visibility:public"], deps = ESBUILD_CONFIG_DEPS, ) diff --git a/client/web/dev/BUILD.bazel b/client/web/dev/BUILD.bazel index 331e8f6375a..04db2391132 100644 --- a/client/web/dev/BUILD.bazel +++ b/client/web/dev/BUILD.bazel @@ -1,3 +1,4 @@ +load("@aspect_rules_esbuild//esbuild:defs.bzl", "esbuild") load("@aspect_rules_js//js:defs.bzl", "js_library") load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("//dev:defs.bzl", "ts_project") @@ -36,7 +37,6 @@ ts_project( "utils/should-compress-response.ts", "utils/success-banner.ts", ], - module = "commonjs", tsconfig = ":tsconfig", visibility = ["//client/web:__subpackages__"], deps = [ @@ -66,10 +66,41 @@ ts_project( js_library( name = "esbuild-config-production", srcs = ["esbuild/bazel/esbuild.bazel.production.js"], - visibility = ["//client:__subpackages__"], deps = [ "//client/build-config:build-config_lib", "//client/web:node_modules/@sourcegraph/build-config", "//client/web/dev", ], ) + +esbuild( + name = "esbuild-config-production_bundle", + srcs = [ + ":esbuild-config-production", + ], + entry_point = "esbuild/bazel/esbuild.bazel.production.js", + external = [ + "fsevents", + "pnpapi", + "../../../../postcss.config", + + # suppress esbuild require-resolve-not-external warnings + "esbuild", + "path-browserify", + "monaco-yaml/lib/esm/monaco.contribution", + "monaco-yaml/lib/esm/yaml.worker", + "rxjs/_esm5/internal/OuterSubscriber", + "rxjs/_esm5/internal/util/subscribeToResult", + "rxjs/_esm5/internal/util/subscribeToArray", + "rxjs/_esm5/internal/Observable", + ], + format = "cjs", + platform = "node", + sourcemap = False, + splitting = False, + target = "es2022", + visibility = ["//client:__subpackages__"], + deps = [ + "//:postcss_config_js", + ], +) diff --git a/client/web/scripts/BUILD.bazel b/client/web/scripts/BUILD.bazel index c2fea2ccee0..7a29f664976 100644 --- a/client/web/scripts/BUILD.bazel +++ b/client/web/scripts/BUILD.bazel @@ -6,7 +6,6 @@ ts_project( srcs = [ "report-bundle-diff.ts", ], - module = "commonjs", tsconfig = "//client/web:tsconfig", deps = [ "//:node_modules/@types/shelljs", diff --git a/client/web/src/end-to-end/BUILD.bazel b/client/web/src/end-to-end/BUILD.bazel index 92cd1eed2b3..1c27b03869f 100644 --- a/client/web/src/end-to-end/BUILD.bazel +++ b/client/web/src/end-to-end/BUILD.bazel @@ -25,7 +25,6 @@ ts_project( "utils/initEndToEndTest.ts", ], tsconfig = ":tsconfig", - use_preset_env = False, deps = [ "//:node_modules/@types/mocha", "//:node_modules/@types/mockdate", @@ -44,7 +43,6 @@ ts_project( "frontend-platform/theme-switcher.test.ts", ], tsconfig = ":tsconfig", - use_preset_env = False, deps = [ ":end-to-end", "//:node_modules/@types/lodash", diff --git a/client/web/src/integration/BUILD.bazel b/client/web/src/integration/BUILD.bazel index 0a730c4b2ec..d99832d8e85 100644 --- a/client/web/src/integration/BUILD.bazel +++ b/client/web/src/integration/BUILD.bazel @@ -28,7 +28,6 @@ ts_project( "temporarySettingsContext.ts", "utils.ts", ], - module = "commonjs", tsconfig = ":tsconfig", deps = [ "//:node_modules/@codemirror/view", @@ -84,7 +83,6 @@ ts_project( "sign-in.test.ts", ], tsconfig = ":tsconfig", - use_preset_env = False, deps = [ ":integration", "//:node_modules/@types/lodash", diff --git a/dev/babel.bzl b/dev/babel.bzl deleted file mode 100644 index cc185d60d5d..00000000000 --- a/dev/babel.bzl +++ /dev/null @@ -1,95 +0,0 @@ -"Babel rule" - -load("@aspect_rules_js//js:defs.bzl", "js_library") -load("@npm//:@babel/cli/package_json.bzl", "bin") - -def babel(name, srcs, module = None, use_preset_env = True, **kwargs): - """A wrapper around Babel CLI - - Args: - name: A unique name for this target - - srcs: A list of sources - - module: If specified, sets BABEL_MODULE environment variable to this value - - use_preset_env: Controls if we transpile TS sources with babel-preset-env. - If set to False, sets the DISABLE_PRESET_ENV environment variable to "true". - - **kwargs: Additional arguments to pass to the rule - """ - - # rules_js runs in the execroot under the output tree in bazel-out/[arch]/bin - execroot = "../../.." - - visibility = kwargs.pop("visibility", ["//visibility:public"]) - source_map = kwargs.pop("source_map", True) - - ts_srcs = [] - outs = [] - data = kwargs.pop("data", []) - deps = kwargs.pop("deps", []) - - # Collect the srcs to compile and expected outputs - for src in srcs: - # JSON does not need to be compiled - if src.endswith(".json"): - data.append(src) - continue - - # dts are only for type-checking and not to be compiled - if src.endswith(".d.ts"): - continue - - if not (src.endswith(".ts") or src.endswith(".tsx")): - fail("babel example transpiler only supports source .ts[x] files, got: %s" % src) - - ts_srcs.append(src) - - # Predict the output paths where babel will write - js_out = src.replace(".tsx", ".js").replace(".ts", ".js") - - outs.append(js_out) - if source_map: - outs.append(js_out + ".map") - - # see https://babeljs.io/docs/en/babel-cli - args = [ - native.package_name(), - "--config-file", - "{}/$(location {})".format(execroot, "//:babel_config"), - "--source-maps", - "true" if source_map else "false", - "--extensions", - ".ts,.tsx", - "--out-dir", - "{}/{}".format(".", native.package_name()), - ] - - env = {} - if module != None: - env["BABEL_MODULE"] = module - - if use_preset_env == False: - env["DISABLE_PRESET_ENV"] = "true" - - bin.babel( - name = "{}_lib".format(name), - progress_message = "Compiling {}:{}".format(native.package_name(), name), - srcs = ts_srcs + [ - "//:babel_config", - "//:package_json", - ], - outs = outs, - args = args, - env = env, - **kwargs - ) - - js_library( - name = name, - srcs = outs, - deps = deps, - data = data, - visibility = visibility, - ) diff --git a/dev/defs.bzl b/dev/defs.bzl index 17ac8d8f93b..1cba01e286c 100644 --- a/dev/defs.bzl +++ b/dev/defs.bzl @@ -1,5 +1,6 @@ "Bazel rules" +load("@aspect_rules_swc//swc:defs.bzl", "swc") load("@bazel_skylib//lib:partial.bzl", "partial") load("@bazel_skylib//rules:expand_template.bzl", "expand_template") load("@aspect_rules_js//npm:defs.bzl", _npm_package = "npm_package") @@ -8,12 +9,11 @@ load("@aspect_rules_jest//jest:defs.bzl", _jest_test = "jest_test") load("@aspect_rules_js//js:defs.bzl", "js_binary") load("//dev:eslint.bzl", "eslint_test_with_types", "get_client_package_path") load(":sass.bzl", _sass = "sass") -load(":babel.bzl", _babel = "babel") sass = _sass # TODO move this to `ts_project.bzl` -def ts_project(name, srcs = [], deps = [], use_preset_env = True, **kwargs): +def ts_project(name, srcs = [], deps = [], module = "es6", **kwargs): """A wrapper around ts_project Args: @@ -23,7 +23,7 @@ def ts_project(name, srcs = [], deps = [], use_preset_env = True, **kwargs): deps: A list of dependencies - use_preset_env: Controls if we transpile TS sources with babel-preset-env + module: The module type to use for the project (es6 or commonjs) **kwargs: Additional arguments to pass to ts_project """ @@ -43,18 +43,27 @@ def ts_project(name, srcs = [], deps = [], use_preset_env = True, **kwargs): visibility = kwargs.pop("visibility", ["//visibility:public"]) # Add standard test libraries for the repo test frameworks - if kwargs.get("testonly", False): + testonly = kwargs.get("testonly", False) + if testonly: deps = deps + [d for d in [ "//:node_modules/@jest/globals", "//:node_modules/@types/mocha", "//:node_modules/@jest/expect", ] if not d in deps] + transpiler = partial.make( + swc, + swcrc = kwargs.pop("swcrc", "//:.swcrc"), + # Test code using jest.mock needs to be transpiled to CommonJS. + args = ["--config-json", '{"module": {"type": "commonjs"}}'] if module == "commonjs" else [], + ) + # Default arguments for ts_project. _ts_project( name = name, srcs = srcs, deps = deps, + transpiler = transpiler, # tsconfig options, default to the root tsconfig = kwargs.pop("tsconfig", "//:tsconfig"), @@ -65,16 +74,6 @@ def ts_project(name, srcs = [], deps = [], use_preset_env = True, **kwargs): source_map = kwargs.pop("source_map", True), preserve_jsx = kwargs.pop("preserve_jsx", None), visibility = visibility, - - # use babel as the transpiler - transpiler = partial.make( - _babel, - use_preset_env = use_preset_env, - module = kwargs.pop("module", None), - tags = kwargs.get("tags", []), - visibility = visibility, - testonly = kwargs.get("testonly", None), - ), supports_workers = 0, # Allow any other args diff --git a/dev/mocha.bzl b/dev/mocha.bzl index 8f508ae9f76..c493579f232 100644 --- a/dev/mocha.bzl +++ b/dev/mocha.bzl @@ -18,9 +18,6 @@ NON_BUNDLED = [ # UMD modules "jsonc-parser", - # Dependencies with bundling issues - "@sourcegraph/build-config", - # Used by require.resolve "axe-core", ] @@ -31,7 +28,6 @@ NON_BUNDLED_DEPS = [ "//:node_modules/puppeteer", "//:node_modules/@axe-core/puppeteer", "//:node_modules/axe-core", - "//client/web:node_modules/@sourcegraph/build-config", ] def mocha_test(name, tests, deps = [], args = [], data = [], env = {}, is_percy_enabled = False, **kwargs):