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 | |
Googler | cb395cb | 2020-12-21 10:33:20 -0800 | [diff] [blame] | 8 | This page describes the features, rules, and workflows related to building |
| 9 | and testing JavaScript outputs. |
Googler | d355b55 | 2019-04-24 11:33:05 -0700 | [diff] [blame] | 10 | |
Googler | cb395cb | 2020-12-21 10:33:20 -0800 | [diff] [blame] | 11 | **Note:** The Bazel team has not fully verified and does not officially support |
| 12 | the features and workflows described on this page. |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 13 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 14 | ## Overview |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 15 | |
Googler | cb395cb | 2020-12-21 10:33:20 -0800 | [diff] [blame] | 16 | Bazel supports an incremental and customizable means of building and |
| 17 | testing JavaScript outputs from JavaScript and TypeScript sources. Bazel also |
| 18 | supports build steps needed for frameworks such as Angular. |
| 19 | |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 20 | Bazel rules for building JavaScript outputs are split into three layers, since |
| 21 | you can use JavaScript without TypeScript, and TypeScript without Angular. |
Googler | cb395cb | 2020-12-21 10:33:20 -0800 | [diff] [blame] | 22 | This page assumes you are already familiar with Bazel and uses the |
Googler | d355b55 | 2019-04-24 11:33:05 -0700 | [diff] [blame] | 23 | [Angular for Bazel sample project](https://github.com/angular/angular-bazel-example) |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 24 | to illustrate the recommended configuration. You can use the sample project as a |
| 25 | 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] | 26 | |
Jingwen Chen | 0f4544d | 2018-12-14 16:28:16 -0800 | [diff] [blame] | 27 | If you're new to Bazel, take a look at the ["Getting |
| 28 | Started"](getting-started.html) material before proceeding. |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 29 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 30 | ## Setting up your environment |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 31 | |
| 32 | To set up your environment for building JavaScript outputs with Bazel, do the |
| 33 | following: |
| 34 | |
Googler | 817e220 | 2020-11-20 19:14:47 -0800 | [diff] [blame] | 35 | ### Installing Bazel |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 36 | |
Googler | d355b55 | 2019-04-24 11:33:05 -0700 | [diff] [blame] | 37 | You can either [Install Bazel](install.html) following the same steps that you |
| 38 | would for backend development, or you can install NodeJS with npm and run |
Vladimir Pouzanov | 7df3716 | 2021-01-27 07:19:40 -0800 | [diff] [blame] | 39 | `npm install -g @bazel/bazelisk`. |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 40 | |
Googler | 817e220 | 2020-11-20 19:14:47 -0800 | [diff] [blame] | 41 | ### Installing iBazel |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 42 | |
| 43 | iBazel, or iterative Bazel, is a "watchdog" version of Bazel that automatically |
| 44 | runs whenever your source files change. Use it to auto-run your tests and |
| 45 | auto-refresh the code served by the development server. |
| 46 | |
| 47 | [Install iBazel](https://github.com/bazelbuild/bazel-watcher) globally using |
| 48 | your package manager of choice. The global installation is required so that |
| 49 | iBazel is in your PATH variable. Also install a specific version of iBazel into |
| 50 | your project so that your whole team updates together. For example: |
| 51 | |
| 52 | ``` |
| 53 | npm install --save-dev @bazel/ibazel |
| 54 | npm install --global @bazel/ibazel |
| 55 | ``` |
| 56 | or |
| 57 | |
| 58 | ``` |
| 59 | yarn add -D @bazel/ibazel |
| 60 | yarn global add @bazel/ibazel |
| 61 | ``` |
| 62 | |
| 63 | To use `ibazel`, simply replace `bazel` with `ibazel` in your Bazel commands. |
| 64 | |
Googler | 817e220 | 2020-11-20 19:14:47 -0800 | [diff] [blame] | 65 | ### Configuring the `bazel.rc` file |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 66 | |
| 67 | Any Bazel build flag or option that can be placed on the command line can also |
wyv | 3a634fa | 2020-05-15 06:47:26 -0700 | [diff] [blame] | 68 | be set in the project's [`bazel.rc` file](guide.html#bazelrc) |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 69 | so that it is applied every time Bazel builds or tests the project. |
| 70 | |
| 71 | Based on how you want to share Bazel settings across your project and team(s), |
| 72 | you can use any combination of the following techniques: |
| 73 | |
| 74 | * **To use the same Bazel settings for the project**, create a `tools/bazel.rc` |
| 75 | file at the root of the Bazel workspace. Adding it to the workspace will |
| 76 | check the file into version control and propagate it to others working on |
| 77 | the project as well as the CI system. |
| 78 | |
| 79 | * **To personalize Bazel settings for the project but not share them,** |
| 80 | create a `.bazel.rc` file at the root of the project and add the file to |
| 81 | your `.gitignore` list. |
| 82 | |
| 83 | * **To personalize Bazel settings for all of your projects on your |
| 84 | local machine,** create a `.bazel.rc` file in your home directory. |
| 85 | |
| 86 | Here's an example `tools/bazel.rc` file to share with your team. Modify this |
| 87 | template as needed. |
| 88 | |
| 89 | ``` |
| 90 | ############################### |
| 91 | # Directory structure # |
| 92 | ############################### |
| 93 | |
Googler | d355b55 | 2019-04-24 11:33:05 -0700 | [diff] [blame] | 94 | # Artifacts are typically placed in a directory called "dist" |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 95 | # Be aware that this setup will still create a bazel-out symlink in |
Googler | d355b55 | 2019-04-24 11:33:05 -0700 | [diff] [blame] | 96 | # your project directory, which you must exclude from version control and your |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 97 | # editor's search path. |
Googler | d355b55 | 2019-04-24 11:33:05 -0700 | [diff] [blame] | 98 | build --symlink_prefix=dist/ |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 99 | |
| 100 | ############################### |
| 101 | # Output # |
| 102 | ############################### |
| 103 | |
| 104 | # A more useful default output mode for bazel query, which |
| 105 | # prints "ng_module rule //foo:bar" instead of just "//foo:bar". |
| 106 | query --output=label_kind |
| 107 | |
| 108 | # By default, failing tests don't print any output, it's logged to a |
| 109 | # file instead. |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 110 | test --test_output=errors |
| 111 | |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 112 | ############################### |
| 113 | # Typescript / Angular / Sass # |
| 114 | ############################### |
| 115 | # Make TypeScript and Angular compilation fast, by keeping a few |
| 116 | # copies of the compiler running as daemons, and cache SourceFile |
| 117 | # ASTs to reduce parse time. |
| 118 | build --strategy=TypeScriptCompile=worker --strategy=AngularTemplateCompile=worker |
| 119 | |
| 120 | # Enable debugging tests with --config=debug |
| 121 | test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test_strategy=exclusive --test_timeout=9999 --nocache_test_results |
| 122 | ``` |
| 123 | |
Googler | d355b55 | 2019-04-24 11:33:05 -0700 | [diff] [blame] | 124 | ### Step 4: Linting |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 125 | |
Googler | d355b55 | 2019-04-24 11:33:05 -0700 | [diff] [blame] | 126 | Add the `buildifier` dependency to your project: |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 127 | |
Googler | d355b55 | 2019-04-24 11:33:05 -0700 | [diff] [blame] | 128 | ```sh |
| 129 | npm install --save-dev @bazel/buildifier |
| 130 | ``` |
| 131 | |
| 132 | or if you prefer Yarn, |
| 133 | |
| 134 | ```sh |
| 135 | yarn add -D @bazel/buildifier |
| 136 | ``` |
| 137 | |
| 138 | then add scripts to your `package.json` that run Buildifier. |
| 139 | We've selected a set of enabled warnings here, you could add and remove from |
| 140 | this list. |
| 141 | |
| 142 | ```json |
| 143 | "scripts": { |
| 144 | "bazel:format": "find . -type f \\( -name \"*.bzl\" -or -name WORKSPACE -or -name BUILD -or -name BUILD.bazel \\) ! -path \"*/node_modules/*\" | xargs buildifier -v --warnings=attr-cfg,attr-license,attr-non-empty,attr-output-default,attr-single-file,constant-glob,ctx-actions,ctx-args,depset-iteration,depset-union,dict-concatenation,duplicated-name,filetype,git-repository,http-archive,integer-division,load,load-on-top,native-build,native-package,out-of-order-load,output-group,package-name,package-on-top,positional-args,redefined-variable,repository-name,same-origin-load,string-iteration,unsorted-dict-items,unused-variable", |
| 145 | "bazel:lint": "yarn bazel:format --lint=warn", |
| 146 | "bazel:lint-fix": "yarn bazel:format --lint=fix" |
| 147 | } |
| 148 | ``` |
| 149 | |
| 150 | > The Bazel team is aware that this configuration is not ergonomic. Follow https://github.com/bazelbuild/buildtools/issues/479 |
| 151 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 152 | ## Building JavaScript |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 153 | |
Attila Ol?h | f355615 | 2018-11-26 13:18:26 -0800 | [diff] [blame] | 154 | Use the [`rules_nodejs`](https://github.com/bazelbuild/rules_nodejs) |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 155 | rules to build NodeJS applications and execute JavaScript code within Bazel. You |
| 156 | can execute JavaScript tools in the Bazel toolchain, binary programs, or tests. |
| 157 | The NodeJS rules add the NodeJS runtime to your Bazel project. |
| 158 | |
Googler | d355b55 | 2019-04-24 11:33:05 -0700 | [diff] [blame] | 159 | See the documentation on that site for setup steps and to configure targets. |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 160 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 161 | ## Building TypeScript |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 162 | |
Googler | d355b55 | 2019-04-24 11:33:05 -0700 | [diff] [blame] | 163 | See the https://www.npmjs.com/package/@bazel/typescript package. |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 164 | |
| 165 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 166 | ### Compiling TypeScript inputs (`ts_library`) |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 167 | |
| 168 | The `ts_library` rule compiles one package of TypeScript code at a time. Each |
| 169 | library compiles independently using the `.d.ts` declaration files from its |
| 170 | dependencies. Thus, Bazel will only rebuild a package if the API the package |
| 171 | depends on changes. |
| 172 | |
| 173 | The `ts_library `rule, by default, outputs a `.d.ts` file for each `.ts` source |
| 174 | file input into it, plus an ES5 (devmode) `.js` file to be used as inputs for |
| 175 | rule targets that depend on the current target, including transitively. |
| 176 | |
| 177 | **Tip:** You can try out the `ts_library` rule by running bazel build src in |
| 178 | the [sample project](https://github.com/alexeagle/angular-bazel-example/wiki). |
| 179 | |
| 180 | **Note:** We recommend standardizing your TypeScript settings into a single |
| 181 | `tsconfig.json` file or as few `tsconfig.json` files as possible. |
| 182 | |
| 183 | Note the following: |
| 184 | |
| 185 | * Bazel controls parts of the `tsconfig.json `file that define locations of |
| 186 | input and output files, manage dependencies on typings, and produce |
| 187 | JavaScript output that's readable by downstream tooling. Currently, this |
| 188 | format is unbundled UMD modules, wrapping noth named (non-anonymous) AMD |
| 189 | modules and `commonjs` modules. |
| 190 | |
| 191 | * Bazel may introduce new requirements for your TypeScript code. For example, |
| 192 | Bazel uses the `-declarations` flag to produce `.d.ts` outputs required by |
| 193 | dependent rule targets; your code may require adjustment to not produce |
| 194 | errors when the `-declarations` flag is in use. |
| 195 | |
| 196 | * If your TypeScript builds are slow, consider granularizing the affected rule |
| 197 | target(s) into smaller sub-targets and declaring dependencies between them |
| 198 | appropriately. |
| 199 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 200 | ### Running a development server (`ts_devserver`) |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 201 | |
| 202 | The `ts_devserver` rule brings up a development server from your application |
| 203 | sources. It's intended for use with the `ibazel run` command so that the server |
| 204 | picks up your code changes immediately. The rule injects a `livereload` script |
| 205 | into the browser, which causes the page to auto-refresh with the latest changes |
| 206 | at the completion of each build. |
| 207 | |
| 208 | **Tip:** You can test-drive the development server feature by running |
| 209 | `ibazel run src: devserver` on the [sample project](https://github.com/alexeagle/angular-bazel-example/wiki). |
| 210 | |
| 211 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 212 | ### Testing TypeScript code (`ts_web_test`) |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 213 | |
| 214 | Use the `ts_web_test` rule to execute the Karma test runner. This rule works |
| 215 | best with ibazel so that both the test runner and the browser pick up your |
| 216 | changes at the completion of each build. For faster builds, Bazel bundles your |
| 217 | code and its dependencies into a single JavaScript file delivered to the browser |
| 218 | when the test runner executes. |
| 219 | |
| 220 | If you need to match lots of tests with a target pattern such as bazel test //… |
| 221 | or using CI, run the `ts_web_test` rule with the regular `bazel test` command |
| 222 | instead. Bazel will then launch a headless Chrome instance and exit after a |
| 223 | single run. |
| 224 | |
| 225 | **Tip:** You can test-drive the `ts_web_test` rule by running `ibazel run` or |
| 226 | `bazel run` on the `src/hello-world:test` target in the [sample project](https://github.com/alexeagle/angular-bazel-example/wiki). |
| 227 | |
| 228 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 229 | ## Building Angular inputs |
spomorski | 97a932f | 2018-06-04 10:16:44 -0700 | [diff] [blame] | 230 | |
spomorski | 03ca7d1 | 2018-06-05 11:32:48 -0700 | [diff] [blame] | 231 | 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] | 232 | |