> ## Documentation Index
> Fetch the complete documentation index at: https://site.aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Angular with Bazel

> Learn how to integrate Angular and Bazel using rules_js for better performance and compatibility.

export const BlogPost = ({title, date, authors, tags, image, children}) => {
  const tagList = tags ? tags.split(", ").filter(Boolean) : [];
  const tagSlug = t => t.toLowerCase().replace(/&/g, "").replace(/\+/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
  const formattedDate = date ? new Date(date + "T00:00:00").toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric"
  }) : "";
  return <section className="w-full flex justify-center px-4 py-12 md:py-16">
      <div style={{
    maxWidth: "800px",
    width: "100%"
  }}>
        {image && (typeof image === "string" ? <img noZoom src={image} alt={title} className="w-full rounded-xl mb-8" style={{
    maxHeight: "400px",
    objectFit: "cover"
  }} /> : <div className="blog-post-hero-image">{image}</div>)}
        <h1 className="text-3xl md:text-4xl font-bold text-zinc-900 dark:text-white">
          {title}
        </h1>
        <div className="flex flex-wrap items-center gap-3 mt-4 text-sm text-zinc-500 dark:text-zinc-400">
          {authors && <span>{authors}</span>}
          {authors && formattedDate && <span>·</span>}
          {formattedDate && <span>{formattedDate}</span>}
        </div>
        {tagList.length > 0 && <div className="flex flex-wrap gap-2 mt-3">
            {tagList.map(tag => <a key={tag} href={"/blog/tags/" + tagSlug(tag)} className="px-2 py-0.5 rounded-full text-xs bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 hover:bg-blue-100 dark:hover:bg-blue-900/40 hover:text-blue-700 dark:hover:text-blue-300 transition">
                {tag}
              </a>)}
          </div>}
        <hr className="my-8 border-zinc-200 dark:border-zinc-700" />
        <div className="prose dark:prose-invert max-w-none">{children}</div>
      </div>
    </section>;
};

export const MarketingPage = () => <div className="marketing-page-marker" style={{
  display: "none"
}} />;

export const Section = ({children, className = "", gray = false, dark = false, id}) => <section id={id} className={`w-full flex justify-center px-4 py-16 md:py-24 ${gray ? "bg-gray-50 dark:bg-zinc-900" : dark ? "bg-zinc-900 dark:bg-zinc-950" : ""} ${className}`}>
    <div className="w-full" style={{
  maxWidth: "1140px"
}}>
      {children}
    </div>
  </section>;

<MarketingPage />

<BlogPost title="Angular with Bazel" date="2022-08-30" authors="Jason Bedard" tags="JavaScript, TypeScript, Migration, Bazel">
  <img noZoom src="https://mintcdn.com/aspectbuild/x1L7Iep716jCyJVo/images/blog/hashnode/hn-1c1e687e2a.jpeg?fit=max&auto=format&n=x1L7Iep716jCyJVo&q=85&s=6e8a820ec86a9a9acfbf9be30a034a72" alt="" className="blog-post-cover" width="1600" height="1143" data-path="images/blog/hashnode/hn-1c1e687e2a.jpeg" />

  We recently released [rules\_js](https://github.com/aspect-build/rules_js/) 1.0.0, a faster and more compatible approach to integrating JavaScript tooling under Bazel.

  Now that ng-conf 2022 is kicking off and all our friends are back in Salt Lake City, it's a perfect time to update how Angular and Bazel work well together using rules\_js.

  ## First a bit of background… the Angular CLI

  The [Angular CLI](https://angular.io/cli) is the standard developer tool for Angular applications. From dev server to production bundling, the CLI takes an "all-in-one" approach for a good, lowest-effort developer experience. Under the hood the CLI is primarily a thin wrapper over the [Angular Architect library](https://github.com/angular/angular-cli/tree/14.2.x/packages/angular_devkit/architect). Angular Architect allows various tools to be integrated into the CLI via plugins such as a devserver, webpack bundling, sass integration, and test frameworks, along with the core Angular Compiler (`ngc`). Architect combines those tools into one for the full Angular CLI experience.

  ## Angular Architect in Bazel

  The simplest method of building an Angular application under Bazel is the same as under the Angular CLI: use Angular Architect. Simply invoke the Angular Architect tool from Bazel for the same all-in-one experience as the Angular CLI.

  For example, a single Bazel target to compile an Angular application (named `my-app`, created by the Angular CLI):

  ```python theme={null}
  load("@npm//:@angular-devkit/architect-cli/package_json.bzl", architect_cli = "bin")

  architect_cli.architect(
      name = "my-app",
      args = ["my-app:build"],
      srcs = glob(["**/*.ts", "**/*.sass", "**/*.html"]),
  )
  ```

  See the [Angular Architect example](https://github.com/aspect-build/bazel-examples/tree/main/angular) for a full example.

  But all-in-one is not the idiomatic Bazel style: if anything in the application changes the entire application must recompile. While easier to implement, Angular Architect is not the ideal Bazel experience.

  ## `ngc` in Bazel

  To truly benefit from Bazel the compilation must be split into independent actions that can be individually executed and cached. Each action coordinated by Angular Architect can instead be coordinated by Bazel. When Bazel is coordinating the actions the real benefits of Bazel will be seen such as parallelization, caching, test caching, remote execution/caching and all the other benefits that come with Bazel.

  The primary action is compiling Angular TypeScript including component templates, css and the various annotations such as `@Injectable`. The Angular Compiler (`ngc`), is a drop-in replacement for the TypeScript compiler (`tsc`). The `ts_project` rule can be customized to use `ngc` as the compiler binary.

  Bazel's macros provide a simple way to define your own "syntax sugar". We'll start by declaring the `ngc` compiler target, and an `ng_project` macro that makes it easy to declare these.

  **tools/BUILD.bazel**

  ```python theme={null}
  load("@npm//:@angular/compiler-cli/package_json.bzl", compiler_cli = "bin")
  compiler_cli.ngc_binary(name = "ngc")
  ```

  **tools/ng.bzl**

  ```python theme={null}
  load("@aspect_rules_ts//ts:defs.bzl", "ts_project")

  # Macro to wrap Angular's ngc compiler
  def ng_project(name, **kwargs):
      ts_project(
          name = name,

          # NGC compiler, do not use the standard tsc worker
          tsc = "//tools:ngc",
          supports_workers = False,

          # Any other ts_project() or generic args
          **kwargs
      )
  ```

  Now Angular code can be fully compiled with the `ng_project` macro including TypeScript, component HTML and CSS, directives, `@Injectable`s etc.

  An example of a `ng_project` target:

  **my-app/BUILD.bazel**

  ```python theme={null}
  load("//tools:ng.bzl", "ng_project")

  ng_project(
      name = "my-app",
      srcs = glob(["**/*.ts", "**/*.css", "**/*.html"]),
      deps = [
          "//:node_modules/@angular/core",
          ...
      ],
  )
  ```

  This is a single `ng_project` rule, but an application will most likely be divided into many BUILD files, creating many independently compiled and cached Bazel targets.

  Other features of the Angular CLI such as Sass preprocessing, webpack bundling, a devserver, testing etc. will be configured as independent Bazel targets. Under the hood the tools will be the same as Angular Architect but now coordinated by Bazel. Or, you can swap out some of the tools, essentially making your own custom, incremental build system for Angular just with a few lines of Bazel's macros.

  For example, bundling the application with [rules\_webpack](https://github.com/aspect-build/rules_webpack)

  ```python theme={null}
  load("@aspect_rules_webpack//webpack:defs.bzl", "webpack_bundle")

  webpack_bundle(
      name = "bundle",
      entry_point = "main.js",
      srcs = [":my-app"],
  )
  ```

  For a complete example see the angular-ngc Bazel example: [https://github.com/jbedard/bazel-examples/tree/angular-ngc/angular-ngc](https://github.com/jbedard/bazel-examples/tree/angular-ngc/angular-ngc)
</BlogPost>
