Repository navigation
Discussion: Setting up ts_project with custom path mapping in a monorepo #2298
Description
Activity
- added a commit that references this issue
on Nov 30, 2020 - added a commit that references this issue
on Dec 3, 2020 - added a commit that references this issue
on Dec 11, 2020 @lencioni could you provide more details where tsconfig.json files are defined?
Sure thing @mgenov!
For the most part, we currently have a tsconfig.json file in each of our frontend projects. So with the structure I mentioned above, that would be:
frontend/project-a/tsconfig.json frontend/project-b/tsconfig.json frontend/project-c/tsconfig.jsonWe are using project references, and each of these tsconfig.json files extend from a more centralized config in one of our projects that has our global settings in it like the
rootDirsandpathsstuff I mentioned above. Here's an example of one of these project tsconfig.json files:{ "extends": "../typescript/tsconfig.dts.json", "include": [".", "./**/*.json"], "compilerOptions": { "types": ["jest", "node"] }, "references": [ { "path": "../project-b" } ] }We have created some internal tooling that scans the code in our repo for dependencies (currently using jest-haste-map) and keeps parts of these files like the references and compilerOptions.types up to date automatically.
In a few cases today we have some tsconfig.json files more deeply nested in projects. This is mostly to support things in our repo like cypress, which have some different global types that would conflict with other things.
frontend/project-a/tsconfig.json frontend/project-b/tsconfig.json frontend/project-b/cypress/tsconfig.json frontend/project-c/tsconfig.jsonWe plan to add a lot more tsconfig.json files in our repo, such as in all of our test directories, so that we can improve TypeScript performance and avoid dragging in test-specific global types into production code, but first we have some things we need to solve, such as upgrading our tooling to manage the
excludefield for these deeply nested tsconfig.json files. As we roll this out, I believe we will also have some cycles between our test directories that we will need to resolve as well.Thanks for your feedback @lencioni
I've made a showcase project to try to illustrate this usage but I encountered the following compilation error:
frontend/project-a/index.ts(1,24): error TS2307: Cannot find module ':project-b' or its corresponding type declarations.
when executing
bazel build //frontend/project-a:tsconfig
Could you take a look what could cause the error?
Code showcase is located at: https://github.com/mgenov/bazel-typescript-showcase
I think the baseUrl needs to be a bit different in your repo. I opened a PR that I think fixes it: mgenov/bazel-typescript-showcase#1
Yes, with this change everything is working as expected. Thanks!
- added a commit that references this issue
on Jan 15, 2021 Despite reading all the available documentation I'm still unable to discern what exactly the correct way to do this is.
The documentation only refers to this '../..' stuff:
"compilerOptions": { "rootDirs": [ ".", "../../bazel-out/host/bin/path/to", "../../bazel-out/darwin-fastbuild/bin/path/to", "../../bazel-out/k8-fastbuild/bin/path/to", "../../bazel-out/x64_windows-fastbuild/bin/path/to", "../../bazel-out/darwin-dbg/bin/path/to", "../../bazel-out/k8-dbg/bin/path/to", "../../bazel-out/x64_windows-dbg/bin/path/to", ] }But there's some reference here to a
baseUrlabove in this thread?All I've managed to do is get relative imports like
../gen/core_ento work, and only during compilation, not in VSCode, which is left in the dark about the types here.If the documentation is all to recommend using
ts_project, it seems like it should be easier to use thants_libraryandts_binary, which, based on what I've read online it doesn't seem to be.Can I get clarity on whether 'ts_project' is meant to usable at this point, and how exactly it can be made usable?
Edit: sorry for the tone here, I think I'm hitting a strange edge-case with tsconfig.json where VSCode is not picking it up for some reason.
Reacted by John FirebaughWith my
tsconfig.jsonand myWORKSPACEin the same folder, I can get the code building with thistsconfig.json:{ "extends": "@tsconfig/node14/tsconfig.json", // If building without sandboxing, we need to prevent TypeScript from descending into // Bazel's external folder which contains third-party Bazel dependencies. "exclude": ["external/*"], "compilerOptions": { "baseUrl": ".", "declaration": true, "preserveSymlinks": true, "paths": { "*": [ "*", "bazel-out/x64_windows-fastbuild/bin/*", "bazel-out/x64_windows-dbg/bin/*", "bazel-out/k8-fastbuild/bin/*", "bazel-out/k8-dbg/bin/*", "bazel-out/host/bin/*", "bazel-out/darwin-fastbuild/bin/*", "bazel-out/darwin-dbg/bin/*", ] }, } }
-- with the caveat that for whatever reason this doesn't work for VSCode.
OK -- it looks like though it's compiling successfully, that doesn't mean you can actually run it -- it's failing at runtime, because the require() is failing
Sorry for the spam. I've pushed the repo to https://github.com/Zemnmez/quickcult/tree/broken-monorepo as far as I've managed to get it.
yarn bazel_run //solve:mainshould work but fails at runtime, and VSCode is unable to do code completions.What was the solution for this?
@Zemnmez Did you ever manage to figure out VS Code completions?
@Nick-Mazuk, here's what I did to get auto-completions:
Added the path/root references as mentioned in the docs. I also symlinkednode_modulesin WORKSPACE root directory like this, so vscode can find all the respective npm packages and things like eslint, prettier can work:ln -s /actual/path/to/node_modules node_modulesYou can find the actual path to node_modules by doing -
$(bazel info output_base)/external/npm/node_modulesThe solutions here worked well before
transitions were introduced, but when a rule includes atransition, a generated suffix is added to the output path, such asbazel-out/darwin-fastbuild-ST-4a519fd6d3e4instead of simplybazel-out/darwin-fastbuild.We encountered this with rules_docker introducing transitions on
container_imagein v0.23.0. We aren't benefiting from the transition introduced there, so we are solving this by noop-ing the transition to not introduce an output directory change, for now, but I can imagine where the wider use of platforms and transitions could cause the workaround with hardcoded output directories to become more fragileMaybe not super relevant to this discussion, but if others face this type of issue, the way we noop'ed the transition for
container_imagespecifically is:load("@io_bazel_rules_docker//container:container.bzl", _container = "container") def _noop_image_transition_impl(settings, attr): return settings _noop_image_transition = transition( implementation = _noop_image_transition_impl, inputs = [], outputs = [], ) noop_image_transition_container_image = rule( attrs = _container.image.attrs, executable = True, outputs = _container.image.outputs, toolchains = ["@io_bazel_rules_docker//toolchains/docker:toolchain_type"], implementation = _container.image.implementation, cfg = _noop_image_transition, )
I imagine this could be simpler, but I'm not super familiar with transitions yet.
I've been working to set up Bazel with
ts_projectin a large, custom monorepo (i.e. not using something like Lerna or yarn workspaces). We recently resolved a challenge and I'd like to publicly document it here in case it helps someone else (or if someone sees a better solution for us). Feel free to close this issue.Our monorepo is set up so that we have a "frontend" directory with a lot of projects as subdirectories under "frontend". The structure is roughly like this:
Imports inside the same project use relative paths:
And imports from other projects use custom colon-import paths:
We've been running TypeScript without Bazel for a while, and had these custom imports wired up via the following path mapping in our base tsconfig:
When setting up Bazel, we initially expanded this path mapping to look more like this:
This seemed to work okay for some of our projects, however, once we started getting into more complex projects that depended on types that were built from a different project, we started seeing errors in our Bazel
ts_projectoutput like this:It seemed that these types ended up as
unknowninstead of the actual types we expected.We looked at the .d.ts files that
ts_projectbuilt for these types, and found code that started like this:Was built into this:
We expected the
importpath to look something like"../../some-dependency/foo/bar"but instead got"../../bazel-out/darwin-fastbuild/bin/frontend/some-dependency/foo/bar", which doesn't really exist. As a result, TypeScript infers it asanywhich probably gets converted to an unknown somewhere along the line via the generics used here.After a bit of messing around, we believe that we were able to arrive at an okay solution: add
rootDirsand an extrapathsmapping like this:This configuration causes our special colon imports to be converted from
":some-project"to"frontend/some-project", which then works because of the second"frontend/*"path mapping.The main caveat that we've thought of with this approach is that it means we can't just install and use a package named "frontend", but that seems unlikely to cause us any problems.