Bazel for client/*

See Bazel at Sourcegraph for general bazel info/setup.

Tools

The sourcegraph client projects are setup to compile, bundle and test with Bazel. The tools used within Bazel include:

  • Babel for ts\[x\] transpilation
  • Webpack for bundling
  • Jest, Mocha for testing
  • esbuild for experimental bundling as well as internal tooling
  • Node tools such as graphql-codegen for generating graphql schema types

The Bazel rulesets used to support these include:

See Aspect rules docs for more information on the Bazel rulesets used.

Targets

The primary Bazel targets have been configured roughly aligning with the pnpm workspace projects, while often composed of many sub-targets. The primary targets for client/* pnpm projects are generated using bazel configure. The primary targets include:

  • :{name}_pkg for the npm package representing the pnpm project
  • :test for the Jest unit tests of the project
  • :{name}_lib for compiling non-test *.ts\[x\] files
  • :{name}_tests for compiling unit test *.ts\[x\] files

Other targets may be configured per project such as sass compilation, graphql schema generation etc. See client/*/BUILD.bazel files for more details and examples.

The srcs and deps attributes of the prmiary targets are generated by bazel configure and any manual changes to those attributes will normally be overriden. If manual files/deps must be specified they must end with a # keep comment to ensure bazel configure does not remove them. Currently typescript type-only imports are not supported by bazel configure and must be manually added to the deps attribute, see https://github.com/aspect-build/aspect-cli/issues/404.

Additional BUILD.bazel files may exist throughout subdirectories and is encouraged to create many smaller independently cacheable targets.

Testing

All client tests (of all types such as jest and mocha) can be invoked by bazel test //client/... or individual tests can be specified such as bazel test //client/common:test or bazel test //client/web/src/end-to-end:e2e. Jest tests can be debugged using bazel run --config=debug //client/common:test.

Notes

Currently, it's impossible to use features that Babel will transpile, creating helper methods inside Puppeteer driver.page.evaluate calls. E.g., the for-of syntax transpiled by Babel creates a helper in the module's top-level scope and uses it in the evaluate call. But since the contents of the evaluate call are passed to the eval function inside Puppeteer, it doesn't have the reference to the created helper and fails in the runtime. This is caused by the fact that we uniformly transform all TS files to JS using Babel in Bazel. We will develop an approach that would allow skipping the Babel transpilation step for files executed only in the node environment.

Bundling

The primary client/web bundle targets are:

  • //client/web:bundle
  • //client/web:bundle-dev
  • //client/web:bundle-enterpriseSee client/web/BUILD.

The client/web devserver can be run using bazel run //client/web:devserver

Rule configuration

Most rules used throughout client/* are macros defined in tools/*.bzl to provide a consistent configuration used throughout the repo.

FAQ

I encountered "bazel configure" failures on CI, and I'm not sure how to debug and fix the issue. What steps should I follow to resolve this?

  1. Examine the Bazel output logs: Scroll through and parse the Bazel output logs to find any reported errors, particularly missing modules or type declarations.
  2. Compare local and CI environments: Run the same failing test locally (e.g., bazel test //client/web:web_lib_typecheck_test) and compare the results with those on CI. This can help identify discrepancies between the two environments.
  3. Check for missing types: If you see errors related to missing types or modules in the Bazel logs, it's possible that some required dependencies are missing from your Bazel build file. To fix this, manually add the missing dependencies to the deps attribute in your primary target and include a # keep comment to prevent bazel configure from removing them. Be aware that bazel configure currently does not support TypeScript type-only imports, and these dependencies need to be added manually (see https://github.com/aspect-build/aspect-cli/issues/404).
  4. Update the Bazel configuration: Run bazel configure to update your Bazel build configuration. Note that the srcs and deps attributes of the primary targets are generated by bazel configure, and any manual changes to those attributes without the # keep comment will be overridden.
  5. Leverage additional BUILD.bazel files: You can create additional BUILD.bazel files in subdirectories to break down your project into smaller, independently cacheable targets. This can help improve build performance and simplify your build configuration.

I want to execute a js binary with bazel. How do I do that?

Lets say I want to execute a js binary called rawr . rules_js generates Starlark APIs for all dependencies defined in package.json, so to import the generated api for rawr we can do:

load("@npm//:rawr/package_json.bzl", my_alias = "bin")

With the above statement rules_js generated the following targets for rawr:

See the generated macro implementation here for more details.

Cool? I mean we technically have rawr now available but HTF do we use this? To create a build target using rawr we can now do:

my_alias.rawr (
  # js_binary attributes
  name = "rawr_event"
  srcs = ["earth.js],
  args = ["-event", "asteroid"],
)

To create a test target using rawr we can do (note the _test suffix):

my_alias.rawr_test (
  # js_test attributes
  name = "rawr_event_test"
  srcs = ["earth.js],
  args = ["-event", "asteroid", "-test"],
)

So now we have two targets that will execute the rawr js binary with different arguments:

bazel build //:rawr_event
bazel test //:rawr_event_test

An example of a js binary being executed as a bazel test target is graphql-schema-linter, whose definition can be found in cmd/frontend/graphqlbackend/BUILD.bazel.

How do I build/test Bazel targets of a specific type for all client packages?

Using Bazel, you can build or test specific targets across all client packages. This can be done by leveraging Bazel's query and test commands.The test command is used to build and run all test targets. However, with the help of query, we can filter out the specific targets we want to build and test.

For example, to check all Typescript types across all client packages locally, use:

bazel test `bazel query 'attr("name", ".*_typecheck_test", //...)'`
  1. bazel test: Commands Bazel to build and run specified test targets.
  2. bazel query 'attr("name", ".*_typecheck_test", //...)': Sub-command that specifies the targets for test. It queries for all targets whose name ends with _typecheck_test in all packages (//...).

If you want to run tests for a specific package, replace //... with the package path like //client/vscode....

bazel test `bazel query 'attr("name", ".*_typecheck_test", //client/vscode...)'`

How do I run ESLint on a client package?

Similar to the above, we can use bazel query to find all target names ending with _eslint and test them:

bazel test `bazel query 'attr("name", ".*_eslint$", //client/wildcard/...)'`