| [](https://circleci.com/gh/angular/angular-bazel-example) | |
| # Example Angular monorepo using Bazel | |
| There are a few ways to use Angular with Bazel. See https://bazelbuild.github.io/rules_nodejs/examples#angular for an overview of all the options. | |
| This example demonstrates the Google-internal toolchain, which is fast but not very compatible with existing applications. | |
| ## Guide to the example | |
| This example is a monorepo, meant to show many different features and integrations that we expect are generally useful for enterprise use cases. | |
| - **Angular CLI**: you can use the `ng` command to run build, serve, test, and e2e | |
| - **Angular Libraries**: to maximize build incrementality, each Angular module is compiled as a separate step. | |
| This lets us re-use Angular libraries without having to publish them as npm packages. See `src/todos` for a typical `NgModule` compiled as a library for use in the application, using the `ts_project` rule in the `BUILD.bazel` file. | |
| - **TypeScript Libraries**: see `src/lib` for a trivial example of a pure-TS library that's consumed in the application, using the `ts_project` rule in the `BUILD.bazel` file. | |
| - **Sass**: we use Sass for all styling. | |
| Angular components import Sass files, and these are built by Bazel as independent processes calling the modern Sass compiler (written in Dart). | |
| - **Material design**: see `src/material` where we collect the material modules we use. | |
| - **Redux-style state management**: see `src/reducers` where we use the [NgRx Store](https://ngrx.io/guide/store). | |
| - **Lazy loading**: in production mode, the application is served in chunks. | |
| Run `ng serve --prod` | |
| - **Differential loading**: in production mode, we load a pair of `<script>` tags. | |
| Modern browsers will load code in the ES2015 syntax, which is smaller and requires fewer polyfills. Older browsers will load ES5 syntax. | |
| - **Docker**: see below where we package up the production app for [deployment](#deployment) on Kubernetes. | |
| - **Progressive Web App**: support for service worker and the app can be installed on phones and desktops (also has 90+ Lighthouse score) | |
| This example is deployed at https://bazel.angular.io/example | |
| ## Installation | |
| You only need to install one build tool, and which one you choose typically depends on what kind of development you do most often. | |
| If you're a frontend developer, you should install NodeJS and yarn. | |
| The `package.json` file has an `engines` section which indicates the range of NodeJS and yarn versions that you could use. | |
| You simply run `yarn` commands shown below, and don't need to install Bazel or any other dependencies. | |
| If you're a full-stack developer, you might be using Bazel for your backend already. | |
| In this case, you should install Bazel following instructions at http://bazel.build. | |
| Also install `ibazel`, which is a watch mode for Bazel not included in the standard distribution. See https://github.com/bazelbuild/bazel-watcher#installation. | |
| You should have a `.bazelversion` file which will ensure Bazel prints an error if your Bazel version is not in the supported range. | |
| You simply run `bazel` commands shown below, and don't need to install NodeJS, yarn, or any other dependencies. | |
| ## Development | |
| First we'll run the development server: | |
| ```bash | |
| $ ng serve | |
| # or | |
| $ ibazel run //src:devserver | |
| ``` | |
| This runs in "watch mode", which means it will watch any files that are inputs to the `devserver`, and when they change it will ask Bazel to re-build them. | |
| When the re-build is finished, it will trigger a LiveReload in the browser. | |
| This command prints a URL on the terminal. Open that page to see the demo app running. | |
| Now you can edit one of the source files (`src/lib/file.ts` is an easy one to understand and see the effect). | |
| As soon as you save a change, the app should refresh in the browser with the new content. | |
| Our intent is that this time is less than two seconds, even for a large application. | |
| Control-C twice to kill the `devserver`. | |
| ## Testing | |
| We can also run all the unit tests: | |
| ```bash | |
| $ ng test | |
| # or | |
| $ bazel test //src/... | |
| ``` | |
| Or run the end-to-end tests: | |
| ```bash | |
| $ ng e2e | |
| # or | |
| $ bazel test //e2e/... | |
| ``` | |
| In this example, there is a unit test for the `hello-world` component which uses the `@npm//karma` and `web_test_suite` rule. | |
| There are also protractor e2e tests for both the `prodserver` and `devserver` which use the `protractor_web_test_suite` rule. | |
| Note: Bazel will only re-run the tests whose inputs changed since the last run. | |
| ## Production | |
| We can run the application in production mode, where the code has been bundled and optimized. | |
| This can be slower than the development mode, because any change requires re-optimizing the app. | |
| This example uses Rollup and Uglify, but other bundlers can be integrated with Bazel. | |
| ```bash | |
| $ ng serve --prod | |
| # or | |
| $ bazel run //src:prodserver | |
| ``` | |
| ### Code splitting | |
| The production bundle is code split and routes such as `/` and `/todos` are lazy loaded. | |
| Code splitting is handled by the rollup_bundle rule which now supports the new code splitting feature in rollup. | |
| Note: code splitting is _not_ supported in development mode yet so the `//src:devserver` target does not serve a code split bundle. | |
| The dynamic `import()` statements will resolve to modules that are served in the initial JS payload. | |
| ## Npm dependencies | |
| Having a local `node_modules` folder setup by `yarn` or `npm` is not necessary when building this example with Bazel. | |
| This example makes use of Bazel managed npm dependencies (https://bazelbuild.github.io/rules_nodejs/dependencies.html) which means Bazel will setup the npm dependencies in your `package.json` for you outside of your local workspace for use in the build. | |
| However, you may still want to run `yarn` or `npm` to manually setup a local `node_modules` folder for editor and tooling support. | |
| ## Deployment | |
| ### Firebase | |
| We use the standard firebase deploy command. | |
| Run `yarn deploy` to release changes to bazel.angular.io. | |
| ### Kubernetes Engine | |
| We use Bazel's docker support to package up our production server for deployment. | |
| Each time the app changes, we'll get a slim new docker layer with just the modified files, keeping the round-trip for deployment incremental and fast. | |
| This example is configured to run on Google Kubernetes Engine, so we can have an elastic pool of backend machines behind a load balancer. | |
| This setup is more expensive to operate than something like Firebase Functions where the backend code is spun up on-demand, but is also more adaptable to scenarios like backend servers that need to run other binaries on the machine. | |
| The application is currently live at http://35.197.115.230/ | |
| To run it under docker: | |
| ```bash | |
| $ bazel run src:nodejs_image -- --norun | |
| $ docker run --rm -p 8080:8080 bazel/src:nodejs_image | |
| ``` | |
| Deploy to production: | |
| 1. Install gcloud and kubectl | |
| 1. Authenticate to the Google Container Registry | |
| `gcloud auth configure-docker` | |
| 1. Authenticate to Kubernetes Engine | |
| `gcloud container clusters get-credentials angular-bazel-example --zone=us-west1-a` | |
| 1. For the first deployment: `bazel run :deploy.create` | |
| 1. To update: `bazel run :deploy.replace` | |
| Tips: | |
| **Run the binary without docker** | |
| ```bash | |
| $ bazel run src:nodejs_image.binary | |
| ``` | |
| **What's in the image?** | |
| ```bash | |
| $ bazel build src:nodejs_image && file-roller dist/bin/src/nodejs_image-layer.tar | |
| ``` | |
| **Tear down all running docker containers** | |
| ```bash | |
| $ docker rm -f $(docker ps -aq) | |
| ``` | |
| **Hop into the running image on kubernetes** | |
| ```bash | |
| $ kubectl exec angular-bazel-example-prod-3285254973-ncv3g -it -- /bin/bash | |
| ``` |