Skip to content

Instantly share code, notes, and snippets.

@dzintars
Last active December 4, 2021 05:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dzintars/3db8c970867594a5b7d2b6346afb9465 to your computer and use it in GitHub Desktop.
Save dzintars/3db8c970867594a5b7d2b6346afb9465 to your computer and use it in GitHub Desktop.
Personal notes on Bazel Typescript setups

Bazel Typescript

To avoid ../../../../../../filename.ts garbage in the source code you can use Typescript Path Aliases. This approach not only cleans up your imports but also helps when making refacoring and moving packages/modules around. No matter where you will place your package, all imports allways will be resolvable.

But this is not enought at runtime. For example if you want to run WebPack dev-server. By default webpack does not know how to resolve those imports. You need to tweak WebPack config

Other resources:

angular/angular-bazel-example#484

https://github.com/dataform-co/dataform

https://medium.com/jspoint/typescript-compilation-the-typescript-compiler-4cb15f7244bc

https://medium.com/jspoint/typescript-module-system-5022cac310f6

https://medium.com/jspoint/introduction-to-node-js-a-beginners-guide-to-node-js-and-npm-eca9c408f9fe

https://www.npmjs.com/package/tsconfig-paths

There are also plugins for the Rollup and some less popular ones.

Extending tsconfig.json

When working in monorepo, you probably can have different TS projects, like libraries, components, helper utils, programms. Every of those requires different configs, but some configs can be common for all components as example. Or even there could be some common configs for the whole monorepo as example compilerOptions.paths property. This is where extending tsconfig comes into play. Make a root/parent level tsconfig and place all the common settings there. Then make some child tsconfig and place "extends": "../../../tsconfig.base.json" in top of the config. This basically says to inherit all the configuration from the root/base config. Then you can override or add any additional properties.

But in order to make this work in Bazel you need to use ts_config rule which is required if you are extending tsconfig:

load("@npm//@bazel/typescript:index.bzl", "ts_config")

ts_config(
    name = "tsconfig",
    src = "tsconfig.json",
    deps = [
        "//:tsconfig.base.json",
    ],
)

In my case i have tsconfig.base.json placed at the root of my monorepo and i do export it to make it available to any subpackage.

// BUILD.bazel
....
exports_files(
    [
        "tsconfig.base.json",
        "package.json",
    ],
    visibility = ["//visibility:public"],
)

This way i can use it anywhere downstream by including it in ts_config deps like "//:tsconfig.base.json",.

@dzintars
Copy link
Author

dzintars commented Oct 3, 2020

Hmmm...
When i add:

   "esModuleInterop": true,

Article

» bazel build //tools/webpack:webpack fails with:

BTW: you can use shortcut if rule name match directory name bazel build //tools/webpack

» bazel build //tools/webpack:webpack
INFO: Invocation ID: 91ce6d3d-4804-4265-b3ef-d6d89e5067b9
INFO: Analyzed target //tools/webpack:webpack (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
INFO: Writing explanation of rebuilds to 'bazel-explain.txt'
ERROR: /home/dzintars/code/github.com/dzintars/prime/tools/webpack/BUILD.bazel:6:11: Compiling TypeScript (devmode) //tools/webpack:webpack failed (Exit 1)
tools/webpack/src/bundle.ts:8:18 - error TS2349: This expression is not callable.
  Type 'typeof import("/home/dzintars/.cache/bazel/_bazel_dzintars/9d2bbefc3ec5d528aec11b4c4ea11d3f/execroot/oswee/external/npm/node_modules/@types/webpack/index.d.ts")' has no call signatures.

8 const compiler = webpack(config)
                   ~~~~~~~

  tools/webpack/src/bundle.ts:1:1
    1 import * as webpack from 'webpack'
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Type originates at this import. A namespace-style import cannot be called or constructed, and will cause a failure at runtime. Consider using a default import or import require here instead.
tools/webpack/src/devserver.ts:13:18 - error TS2349: This expression is not callable.
  Type 'typeof import("/home/dzintars/.cache/bazel/_bazel_dzintars/9d2bbefc3ec5d528aec11b4c4ea11d3f/execroot/oswee/external/npm/node_modules/@types/webpack/index.d.ts")' has no call signatures.

13 const compiler = webpack(fixedConfig)
                    ~~~~~~~

  tools/webpack/src/devserver.ts:2:1
    2 import * as webpack from 'webpack'
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Type originates at this import. A namespace-style import cannot be called or constructed, and will cause a failure at runtime. Consider using a default import or import require here instead.
tools/webpack/src/devserver.ts:48:20 - error TS2351: This expression is not constructable.
  Type 'typeof WebpackDevServer' has no construct signatures.

48 const server = new webpackDevServer(singleSimultaneousCompiler, devServerOptions)
                      ~~~~~~~~~~~~~~~~

  tools/webpack/src/devserver.ts:1:1
    1 import * as webpackDevServer from 'webpack-dev-server'
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Type originates at this import. A namespace-style import cannot be called or constructed, and will cause a failure at runtime. Consider using a default import or import require here instead.

Target //tools/webpack:webpack failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 0.399s, Critical Path: 0.28s
INFO: 0 processes.
FAILED: Build did NOT complete successfully

Can't find the source, but removing * as from the imports helps in this situation. And in opposite, adding * as will solve the other issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment