spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 1 | --- |
| 2 | layout: documentation |
| 3 | title: Building JavaScript Outputs |
| 4 | --- |
| 5 | |
| 6 | # Building JavaScript Outputs |
| 7 | |
| 8 | Bazel supports an incremental and customizable means of building and testing |
| 9 | JavaScript outputs from JavaScript, TypeScript, and Angular sources. |
| 10 | |
| 11 | **Note:** This document describes Bazel features and workflows that are useful, |
| 12 | but the Bazel team has not fully verified and does not officially support |
| 13 | these features and workflows. |
| 14 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 15 | ## Contents |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 16 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 17 | * [Overview](#overview) |
| 18 | * [Setting up your environment](#setting-up-your-environment) |
| 19 | * [Step 1: Installing Bazel](#step-1-installing-bazel) |
| 20 | * [Step 2: Installing iBazel](#step-2-installing-ibazel) |
| 21 | * [Step 3: Configuring the `bazel.rc` file](#step-3-configuring-the-bazel-rc-file) |
| 22 | * [Step 4: (Optional) Setting up Continuous Integration (CI)](#step-4-optional-setting-up-continuous-integration-ci) |
| 23 | * [Building JavaScript inputs](#building-javascript) |
| 24 | * [Building TypeScript inputs](#building-typescript) |
| 25 | * [Compiling TypeScript inputs (`ts_library`)](#compiling-typescript-inputs-ts_library) |
| 26 | * [Running a development server (`ts_devserver`)](#running-a-development-server-ts_devserver) |
| 27 | * [Testing TypeScript code (`ts_web_test`)](#testing-typescript-code-ts_web_test) |
| 28 | * [Building Angular inputs](#building-angular-inputs) |
| 29 | |
| 30 | ## Overview |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 31 | |
| 32 | Bazel rules for building JavaScript outputs are split into three layers, since |
| 33 | you can use JavaScript without TypeScript, and TypeScript without Angular. |
| 34 | This document assumes you are already familiar with Bazel and uses the |
| 35 | [Angular for Bazel sample project](https://github.com/alexeagle/angular-bazel-example) |
| 36 | to illustrate the recommended configuration. You can use the sample project as a |
| 37 | starting point and add your own code to it to start building with Bazel. |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 38 | |
| 39 | If you're new to Bazel, take a look at the ["Getting Started"](https://docs.bazel.build/versions/master/getting-started.html) |
| 40 | material before proceeding. |
| 41 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 42 | ## Setting up your environment |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 43 | |
| 44 | To set up your environment for building JavaScript outputs with Bazel, do the |
| 45 | following: |
| 46 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 47 | ### Step 1: Installing Bazel |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 48 | |
| 49 | If you have not already done so, [Install Bazel](https://docs.bazel.build/versions/master/install.html). |
| 50 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 51 | ### Step 2: Installing iBazel |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 52 | |
| 53 | iBazel, or iterative Bazel, is a "watchdog" version of Bazel that automatically |
| 54 | runs whenever your source files change. Use it to auto-run your tests and |
| 55 | auto-refresh the code served by the development server. |
| 56 | |
| 57 | [Install iBazel](https://github.com/bazelbuild/bazel-watcher) globally using |
| 58 | your package manager of choice. The global installation is required so that |
| 59 | iBazel is in your PATH variable. Also install a specific version of iBazel into |
| 60 | your project so that your whole team updates together. For example: |
| 61 | |
| 62 | ``` |
| 63 | npm install --save-dev @bazel/ibazel |
| 64 | npm install --global @bazel/ibazel |
| 65 | ``` |
| 66 | or |
| 67 | |
| 68 | ``` |
| 69 | yarn add -D @bazel/ibazel |
| 70 | yarn global add @bazel/ibazel |
| 71 | ``` |
| 72 | |
| 73 | To use `ibazel`, simply replace `bazel` with `ibazel` in your Bazel commands. |
| 74 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 75 | ### Step 3: Configuring the `bazel.rc` file |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 76 | |
| 77 | Any Bazel build flag or option that can be placed on the command line can also |
Wayou Liu | ccbd3e6 | 2018-08-10 07:03:11 -0700 | [diff] [blame] | 78 | be set in the project's [`bazel.rc` file](https://docs.bazel.build/versions/master/user-manual.html#bazelrc) |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 79 | so that it is applied every time Bazel builds or tests the project. |
| 80 | |
| 81 | Based on how you want to share Bazel settings across your project and team(s), |
| 82 | you can use any combination of the following techniques: |
| 83 | |
| 84 | * **To use the same Bazel settings for the project**, create a `tools/bazel.rc` |
| 85 | file at the root of the Bazel workspace. Adding it to the workspace will |
| 86 | check the file into version control and propagate it to others working on |
| 87 | the project as well as the CI system. |
| 88 | |
| 89 | * **To personalize Bazel settings for the project but not share them,** |
| 90 | create a `.bazel.rc` file at the root of the project and add the file to |
| 91 | your `.gitignore` list. |
| 92 | |
| 93 | * **To personalize Bazel settings for all of your projects on your |
| 94 | local machine,** create a `.bazel.rc` file in your home directory. |
| 95 | |
| 96 | Here's an example `tools/bazel.rc` file to share with your team. Modify this |
| 97 | template as needed. |
| 98 | |
| 99 | ``` |
| 100 | ############################### |
| 101 | # Directory structure # |
| 102 | ############################### |
| 103 | |
| 104 | # Globally cache downloaded artifacts. |
| 105 | build --experimental_repository_cache=~/.bazel_cache/ |
| 106 | test --experimental_repository_cache=~/.bazel_cache/ |
| 107 | run --experimental_repository_cache=~/.bazel_cache/ |
| 108 | |
| 109 | # Don't create bazel-* symlinks in the WORKSPACE directory. These |
| 110 | # symlinks require .gitignore and may scare users. Instead, run |
| 111 | # `bazel info bazel-bin` to find out where the outputs are stored. |
| 112 | build --symlink_prefix=/ |
| 113 | |
| 114 | # Another good choice is to create a dist/ directory. Then you can |
| 115 | # use build --symlink_prefix=dist/ to get folders like dist/bin. |
| 116 | # Be aware that this setup will still create a bazel-out symlink in |
| 117 | # your project directory, which you may need to exclude from the |
| 118 | # editor's search path. |
| 119 | |
| 120 | ############################### |
| 121 | # Output # |
| 122 | ############################### |
| 123 | |
| 124 | # A more useful default output mode for bazel query, which |
| 125 | # prints "ng_module rule //foo:bar" instead of just "//foo:bar". |
| 126 | query --output=label_kind |
| 127 | |
| 128 | # By default, failing tests don't print any output, it's logged to a |
| 129 | # file instead. |
| 130 | |
| 131 | test --test_output=errors |
| 132 | |
| 133 | # Show which actions are running under which workers and print all |
| 134 | # the actions running in parallel. This shows that Bazel runs on all |
| 135 | # cores of a CPU. |
| 136 | build --experimental_ui |
| 137 | test --experimental_ui |
| 138 | |
| 139 | ############################### |
| 140 | # Typescript / Angular / Sass # |
| 141 | ############################### |
| 142 | # Make TypeScript and Angular compilation fast, by keeping a few |
| 143 | # copies of the compiler running as daemons, and cache SourceFile |
| 144 | # ASTs to reduce parse time. |
| 145 | build --strategy=TypeScriptCompile=worker --strategy=AngularTemplateCompile=worker |
| 146 | |
| 147 | # Enable debugging tests with --config=debug |
| 148 | test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test_strategy=exclusive --test_timeout=9999 --nocache_test_results |
| 149 | ``` |
| 150 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 151 | ### Step 4: (Optional) Setting up Continuous Integration (CI) |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 152 | |
| 153 | For building JavaScript outputs with Bazel in a CI setting, it's useful to use a |
| 154 | container as the environment. The [ngcontainer Docker image](https://hub.docker.com/r/angular/ngcontainer/) |
| 155 | is a ready-to-use environment you can use that makes your builds reproducible in |
| 156 | other environments, such as your local machine. This reproducibility is |
| 157 | especially convenient on CircleCI, which lets you choose a Docker image as the |
| 158 | environment for your build. See the example [CircleCI configuration](https://github.com/alexeagle/angular-bazel-example/blob/master/.circleci/config.yml) |
| 159 | in the sample project to learn more. |
| 160 | |
| 161 | **Tip:** When building in a CI environment, add settings to your `bazel.rc` file |
| 162 | that are specific to CI using the `build:ci` and or `test:ci` prefixes. With |
| 163 | this configuration, you can enable those CI-specific options by simply adding |
| 164 | the `--config=ci` argument to your Bazel/iBazel commands. |
| 165 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 166 | ## Building JavaScript |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 167 | |
| 168 | Use the <code>[rules_nodejs](https://github.com/bazelbuild/rules_nodejs)</code> |
| 169 | rules to build NodeJS applications and execute JavaScript code within Bazel. You |
| 170 | can execute JavaScript tools in the Bazel toolchain, binary programs, or tests. |
| 171 | The NodeJS rules add the NodeJS runtime to your Bazel project. |
| 172 | |
| 173 | Most notable NodeJS rules include: |
| 174 | |
| 175 | * `nodejs_binary` - builds an executable program based on JavaScript source |
| 176 | files and an entry point path relative to the output root. To provide extra |
| 177 | inputs to be read at runtime, put them in the data attribute. |
| 178 | |
| 179 | * `jasmine_node_test` - runs JavaScript spec files through the Jasmine test |
| 180 | framework. See the [node_js API documentation](https://bazelbuild.github.io/rules_nodejs/) |
| 181 | for more information. |
| 182 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 183 | ## Building TypeScript |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 184 | |
| 185 | Use the <code>[rules_typescript](https://github.com/bazelbuild/rules_typescript)</code> |
| 186 | rules to build JavaScript outputs from TypeScript inputs. |
| 187 | |
| 188 | To set up your Bazel project for building TypeScript inputs, do the following: |
| 189 | |
| 190 | 1. Make Bazel aware of the TypeScript build rules by adding the following entry |
| 191 | to your `WORKSPACE` file: |
| 192 | |
| 193 | ``` |
| 194 | http_archive( |
| 195 | name = "build_bazel_rules_typescript", |
| 196 | url = "https://github.com/bazelbuild/rules_typescript/archive/v0.13.0.zip", |
| 197 | strip_prefix = "rules_typescript-0.13.0", |
| 198 | ) |
| 199 | |
| 200 | load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace") |
| 201 | |
| 202 | ts_setup_workspace() |
| 203 | |
| 204 | ``` |
| 205 | |
| 206 | 2. Add the `--strategy` settings to your `bazel.rc` file as shown in the |
| 207 | example `.bazel.rc` file in ["Configuring the bazel.rc file"](#step-3-configuring-the-bazel-rc-file). |
| 208 | |
| 209 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 210 | ### Compiling TypeScript inputs (`ts_library`) |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 211 | |
| 212 | The `ts_library` rule compiles one package of TypeScript code at a time. Each |
| 213 | library compiles independently using the `.d.ts` declaration files from its |
| 214 | dependencies. Thus, Bazel will only rebuild a package if the API the package |
| 215 | depends on changes. |
| 216 | |
| 217 | The `ts_library `rule, by default, outputs a `.d.ts` file for each `.ts` source |
| 218 | file input into it, plus an ES5 (devmode) `.js` file to be used as inputs for |
| 219 | rule targets that depend on the current target, including transitively. |
| 220 | |
| 221 | **Tip:** You can try out the `ts_library` rule by running bazel build src in |
| 222 | the [sample project](https://github.com/alexeagle/angular-bazel-example/wiki). |
| 223 | |
| 224 | **Note:** We recommend standardizing your TypeScript settings into a single |
| 225 | `tsconfig.json` file or as few `tsconfig.json` files as possible. |
| 226 | |
| 227 | Note the following: |
| 228 | |
| 229 | * Bazel controls parts of the `tsconfig.json `file that define locations of |
| 230 | input and output files, manage dependencies on typings, and produce |
| 231 | JavaScript output that's readable by downstream tooling. Currently, this |
| 232 | format is unbundled UMD modules, wrapping noth named (non-anonymous) AMD |
| 233 | modules and `commonjs` modules. |
| 234 | |
| 235 | * Bazel may introduce new requirements for your TypeScript code. For example, |
| 236 | Bazel uses the `-declarations` flag to produce `.d.ts` outputs required by |
| 237 | dependent rule targets; your code may require adjustment to not produce |
| 238 | errors when the `-declarations` flag is in use. |
| 239 | |
| 240 | * If your TypeScript builds are slow, consider granularizing the affected rule |
| 241 | target(s) into smaller sub-targets and declaring dependencies between them |
| 242 | appropriately. |
| 243 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 244 | ### Running a development server (`ts_devserver`) |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 245 | |
| 246 | The `ts_devserver` rule brings up a development server from your application |
| 247 | sources. It's intended for use with the `ibazel run` command so that the server |
| 248 | picks up your code changes immediately. The rule injects a `livereload` script |
| 249 | into the browser, which causes the page to auto-refresh with the latest changes |
| 250 | at the completion of each build. |
| 251 | |
| 252 | **Tip:** You can test-drive the development server feature by running |
| 253 | `ibazel run src: devserver` on the [sample project](https://github.com/alexeagle/angular-bazel-example/wiki). |
| 254 | |
| 255 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 256 | ### Testing TypeScript code (`ts_web_test`) |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 257 | |
| 258 | Use the `ts_web_test` rule to execute the Karma test runner. This rule works |
| 259 | best with ibazel so that both the test runner and the browser pick up your |
| 260 | changes at the completion of each build. For faster builds, Bazel bundles your |
| 261 | code and its dependencies into a single JavaScript file delivered to the browser |
| 262 | when the test runner executes. |
| 263 | |
| 264 | If you need to match lots of tests with a target pattern such as bazel test //… |
| 265 | or using CI, run the `ts_web_test` rule with the regular `bazel test` command |
| 266 | instead. Bazel will then launch a headless Chrome instance and exit after a |
| 267 | single run. |
| 268 | |
| 269 | **Tip:** You can test-drive the `ts_web_test` rule by running `ibazel run` or |
| 270 | `bazel run` on the `src/hello-world:test` target in the [sample project](https://github.com/alexeagle/angular-bazel-example/wiki). |
| 271 | |
| 272 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 273 | ## Building Angular inputs |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 274 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 275 | Bazel can build JavaScript outputs from Angular. For instructions, see [Building Angular with Bazel](https://github.com/alexeagle/angular-bazel-example/wiki/Angular-rules). |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 276 | |