Building TypeScript Project with Bazel Translated 100%

oschina Posted at 19:05 on January 14, 2019 (9 paragraphs in total, translation completed on January 25)
Reading 4056
Collection one
top two
Loading

In this article, I will briefly introduce what Bazel is and how we can use it to build a Typescript project. If you are already familiar with what Bazel solves, please skip to the section "Building Typescript with Bazel". You can My Github Find examples on!

Google's Integration Insider

Google manages countless source codes. There are dependencies between each independent project. For example, Google Cloud depends on Angular&Angular Material, Angular Material depends on Angular, and finally they all depend on TypeScript

In Google, projects have version numbers using independent libraries (as well as third-party libraries). Within the company, this greatly simplifies dependency management. Upgrading a library at the same time can affect all dependent projects, which is very convenient. In this way, everyone can benefit from it, such as the latest security update, performance optimization, bug repair. This means that Microsoft will release new Typescript, which will be synchronized to Google as soon as possible. Like you, destructive changes to Typescript will affect many codes!

In order to verify that there is no original code after the library upgrade, it is necessary to rebuild all dependent projects in the internal continuous integration and execute the corresponding tests. Because there are countless TS files and countless lines of TS code, if the control is not good, the whole process may take some time.

In most cases, projects rely on other projects, such as Google Cloud, which relies on its UI and back-end services. When only Typescript is updated, since the background service does not depend on Typescript, we do not want it to rebuild the background service!

 Visitor - Shen Junjian
Translated at 17:13, January 23, 2019
top
zero

What is Bazel

In order to build the project, Google has opened the Bazel tool. It is a powerful tool that can track the dependencies between different packages and build targets. To put it simply, a build is a build rule, such as: "Build a Typescript library"; From a project perspective, a package corresponds to many files in a folder, and it has a clear dependency package. In the example of Google Cloud, Bazel will correspond to the following dependency graph:

To put it simply, each block in the figure above corresponds to a construction target. When one of these blocks changes, Bazel calculates which packages directly or indirectly depend on it and builds them. In the above example, if TypeScript changes, it will build blocks other than back end services.

Here are Bazel's cool features:

1. It has a clever algorithm to calculate dependencies

2. It has a separate technology stack. You can build anything with the same interface. For example, there are already many plug-ins to serve Java, Go, Typescript, Javascript, and so on.

 Visitor - Shen Junjian
Translated on January 23, 2019 17:29
top
one

Let's take a look at the first item. Based on the dependency graph of a project, Bazel will judge which construction goals can be processed in parallel. However, this feature is only effective when the unit test has well defined the input and output items, and they do not produce side effects. We can understand them as "pure functions". This "computable" model has one advantage. It is easy to reduce the amount of computation through parallel and cache methods. Bazel is like this. It caches the output results of independent construction tasks, even in the cloud!

Why emphasize the cache problem on the cloud? If Bazel can build and cache in the cloud, anyone can use this build result. If you are a large company, even small teams can benefit from it. Bazel is not bound to a cloud platform, which means you can get the benefits of caching remote building on Google Cloud, Azure, AWS, or your own devices.

Well, I've talked a lot. Let's take an example!

 Visitor - Shen Junjian
Translated at 17:50 on January 23, 2019
top
zero

Building TypeScript with Bazel

In this example, we build a small ts project and finally generate an es5 js file. This project only has the following modules:

  • Lexer - returns a token array after entering a string

  • Parser - Enter a token array. Return to the extract syntax tree AST

  • Interpreter - accepts an AST and evaluates it

  • Application - wires everything together - passes the program to the lexer, feeds the parser with the lexer’s output, and the interpreter with the produced AST

 Visitor - Shen Junjian
Translated at 18:04, January 23, 2019
top
zero

Configure Workspace Environment

Our project relies on the npm package TypeScript, Bazel, and a Bazel TypeScript rule. This is no different from other projects. So let's go deep into the WORKSPACE configuration file to find out:

 workspace(name = 'lang') http_archive(     name = "build_bazel_rules_typescript",     url = " https://github.com/bazelbuild/rules_typescript/archive/0.21.0.zip ",     strip_prefix = "rules_typescript-0.21.0", ) # Fetch our Bazel dependencies that aren't distributed on npm load("@build_bazel_rules_typescript//:package.bzl", "rules_typescript_dependencies") rules_typescript_dependencies() # Setup TypeScript toolchain load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace") ts_setup_workspace() # Setup the Node.js toolchain load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories", "yarn_install") node_repositories() # Setup Bazel managed npm dependencies with the `yarn_install` rule. yarn_install(   name = "npm",   package_json = "//:package.json",   yarn_lock = "//:yarn.lock", )

You should know that this article is just a simple introduction. In fact, you may never manage Bazel configuration yourself

The language used in the above file is Starlark , you can think of it as a subset of Python language, which is our declaration:

  1. The workspace name is lang

  2. The project uses Bazel's TypeScript rules. Remember that it and our declaration The version numbers of package.json are consistent.

  3. Next, we obtain Bazel's dependencies build_bazel_rules_typescript

  4. Set TypeScript workspace

  5. Set the construction tool chain of Bazel's Node.js It is a set of tools maintained by Bazel team, so we can use Node.js here

  6. Finally, we declare a rule to let Bazel manage npm dependencies!

It doesn't matter if you can't understand the above code, just remember load Almost in Node.js require, The difference is that load can also obtain dependencies from the network address!

 

 Visitor - Shen Junjian
Translated at 19:47 on January 23, 2019
top
zero

Configure Compilation Target

We go deeper into finer granularity. We regard independent modules in the project as Bazel packages. In each package, we define a compilation target. As mentioned above, we regard each package as a folder, which contains the files and a construction rule.

Each folder has one BUILD Or BUILD.bzl File. Let's take a look at the BUILD file in the project:

 package(default_visibility = ["//visibility:public"]) load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") ts_library(   name = "app",   srcs = ["test.ts"],   deps = ["//lexer", "//parser", "//interpreter"], ) load("@build_bazel_rules_nodejs//:defs.bzl", "rollup_bundle") rollup_bundle(   name = "bundle",   entry_point = "test.js",   deps = [":app"], )

We first define whether the package is visible, and then load two Bazel rules:

  • ts_library -Use it to compile Typescript files

  • rollup_bundle -Call Rollup.js to package the modules into a file.

  ts_library Rules are used to build app Objectives of The goal of this app is to connect all modules together It depends on lexer parser , and the  interpreter In these three folders, there are also BUILD The file and content are almost the same as above For example Contents of parser :

 package(default_visibility = ["//visibility:public"]) load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") ts_library(     name = "parser",     srcs = glob(["*.ts"]),     deps = ["//lexer"], )

Here we use glob This module also depends on  lexer Module

Many dependencies have many construction goals, which may lead to confusion. Bazel has a simple function that the dependency diagram must be statically analyzable. We can use Bazel query syntax to directly query the dependency graph. Let me see what it looks like!

The first step is to use yarn  To install package.json  All the dependent packages in, so Bazel is automatically installed.

 yarn

Start bazel (you may need to install graphviz first)

 ./node_modules/.bin/bazel query --output=graph ...  | dot -Tpng > graph.png

The output result of the above command is as follows:

We can see the dependencies between the targets.

 

 Visitor - Shen Junjian
Translated at 10:11 on January 24, 2019
top
zero

Now we can build the whole project:

 ./node_modules/.bin/bazel build :bundle

It can be expected that the above command will take a little time because Bazel loads the dependencies in the workspace. After that, every step should be completed in an instant.

Now let's verify whether the bundle after execution meets the expectations:

 node bazel-bin/bundle.js forty-three

After you have built Bazel goals once, the workspace will produce many symlinks, which contain many building products. If you don't want to, you can go through bazel.rc To configure

 

 

 

 

 Visitor - Shen Junjian
Translated at 10:21 on January 24, 2019
top
zero

monitor mode

How to monitor changes in project files and rebuild them in time? For this purpose, we need to install and run the @ bazel/ibazel package

 # Don’t forget yarn add -D @bazel/ibazel ./node_modules/.bin/ibazel build :app

adopt ibazel  and  bazel The package will rebuild the target when the dependency changes directly or indirectly

Note that in the above command, we build the target of app. This is because in code development, based on Bazel's Rollup.js rule, we cannot directly code to affect :bundle This construction goal. (The implication is that :bundle Through :app Generated)

Now let's create the command abbreviation of npm to replace ./node_modules/.bin/bazel  or  ./node_modules/.bin/ibazel

stay package.json Add the following two sentences:

 {   "name": "bazel-demo",   "license": "MIT",   "scripts": {     "build": "bazel build :bundle",     "watch": "ibazel build :app"   },   "devDependencies": {     "@bazel/bazel": "^0.19.1",     "@bazel/typescript": "0.21.0",     "typescript": "^3.1.6"   } }

Run Now yarn build To rebuild the project or yarn watch To listen and automatically build the project.

 Visitor - Shen Junjian
Translated at 10:32 on January 24, 2019
top
zero

Conclusion

In this article, we focus on Google's construction system Bazel. This article explains what problems Bazel solved to provide us with a fast and flexible construction solution.

In the second part of this article, we demonstrated the construction example through a small Typescript project. We compile a set of files and then use Rollup.js to package and output them as a file. In the example, we explained what is workspace, build target, package, and how to configure them.

So far, we can see Bazel's core capability: static dependency graph, which allows Cache, parallel, etc Simple optimization of. In the next article, we will pay more attention to the implementation of performance!

 Visitor - Shen Junjian
Translated at 10:48 on January 24, 2019
top
one
All translations in this article are only for learning and communication purposes. Please be sure to indicate the translator, source and link of the article when reprinting.
Our translation work follows CC protocol If our work infringes your rights and interests, please contact us in time.
Loading

Comments( one )

 Visitor - Shen Junjian
Visitor - Shen Junjian
There are some mistakes or omissions in the text, but the translation results are not allowed to be modified, 😡
 Back to top
Top