Project References docs/example are missing rootDirs (?) #37257
Description
I'm working on a prototype to rebuild the Bazel TypeScript support on top of Project References, basically following the paragraph
https://www.typescriptlang.org/docs/handbook/project-references.html
Some teams have set up msbuild-based workflows wherein tsconfig files have the same implicit graph ordering as the managed projects they are paired with. If your solution is like this, you can continue to use msbuild with tsc -p along with project references; these are fully interoperable.
where msbuild is replaced by Bazel. Seems very promising so far, however there's a snag.
Looking at Ryan's demo
https://github.com/RyanCavanaugh/project-references-demo
we see that each project uses an outDir setting to avoid sprinkling build outputs in the sources (yay)
https://github.com/RyanCavanaugh/project-references-demo/blob/master/core/tsconfig.json#L4
However in dependent projects there is no matching rootDirs option
https://github.com/RyanCavanaugh/project-references-demo/blob/master/animals/tsconfig.json
According to the README, the empty-sleeves branch deletes the code from the dependent project, so I would expect TS resolves from the .d.ts in the outDir. So I'm not understanding how the compiler could find /lib/core/utilities.d.ts to resolve imports like import { makeRandomName } from '../core/utilities'; that appear in /animals/dog.ts.
Indeed checking out the empty-sleeves branch, it doesn't seem to compile, seems like it's just out-of-date prototype code
tsproject.json:11:5 - error TS5023: Unknown compiler option 'referenceTarget'.
11 "referenceTarget": true
Another way to observe my problem: In my minimal Proof-of-Concept I am forced to specify a rootDirs setting.
https://github.com/alexeagle/ts_composite/blob/master/tsconfig-base.json#L7-L8
and in this case since Bazel creates output directory with the platform name in it, I can't really check in this file and keep the project portable between mac/linux/windows.
So the question is, does TS have some way of resolving the outDir from project references that I'm missing? Or is the rootDirs setting required to make this work?
Activity
alexeagle commentedon Mar 10, 2020
I think this may be asking the same thing as #27572 which was closed without a resolution.
DanielRosenwasser commentedon Mar 10, 2020
I am embarrassed to ask whether you meant to discuss
rootDirs, or if you actually meantrootDir.alexeagle commentedon Mar 10, 2020
copying from a discussion with Daniel, here's an attempt at a simpler explanation:
foo and bar are separate projects where bar references foo.
We are compiling bar, and
def.tscontainsimport from ../foo/abc.If the sources for foo (src/foo/abc.ts) aren't present, this currently fails.
But foo/tsconfig.json has
outDir: "../output/foo"so the compiler could have known to look at../output/foo/abc.d.tsto resolve the import.DanielRosenwasser commentedon Mar 11, 2020
Having chatted on a call, it sounded like you're finding some issue where project references don't play well with a segmented source and output root.
In this scenario, the idea is that
abc.tsimportsdef.tswith a relative import. With project references, we always recommend that users import in a way that will work at runtime. So given the output directory structure, this is fine:Maybe what you were seeing is that in that case, resolution doesn't seem to be jumping to the output
.d.tsfiles - it seems to resolve to the.tsfile.However, I think that at compile-time we must be doing "the right thing" by rebuilding nothing and rechecking against the
.d.tsfiles.You can tell that the original
.tsfiles aren't being rebuilt because the file structure ofbarisn't getting hoisted into the output file structure, and you can test whether TypeScript is building against the.d.tsfiles by makingbar's files massive, and rebuildingfooafter a small change inabc.ts.As a side note: the workaround you were trying for not finding the
.d.tsfiles was to userootDirsto virtualize the source.tsand output.d.tsfiles being in the same place. I don't know how well that would work, since if I recall correctly, project references changes resolution to prefer.tsfiles over.d.tsfiles if it finds both.alexeagle commentedon Mar 13, 2020
It's true that everything is working as intended when the referenced
.tsfiles are resolved.My problem is that those
.tsfiles are not present on disk, like in Ryan'sempty-sleevesexample. That's because my build system (Bazel) allows you to parallelize the build on remote worker machines (or simulates this in a local filesystem sandbox to ensure hermeticity of the build). Each TS project is built in a separate build step and therefore in an isolated sandbox (or remote worker). So the filesystem at compile-time offoolooks likealexeagle commentedon Mar 13, 2020
My last caveat there raises a possible solution: always make the
.tssources of referenced projects available in the Bazel sandbox/remote worker. To fix the brokenness of the incrementality, we'll have to take extra care to make the tsc actions properly behave with--incremental. Of course for changes that affected.d.tswe already had the problem of Bazel re-triggering these build steps which would benefit from --incremental.I'll leave this open as I still think it's reasonable to expect that module resolution should succeed given the structure on disk I pointed to, and given that Ryan had thought of such a scenario in that
empty-sleevesbranch.It turns out that passing
.ts/.tsbuildinfofiles between Bazel build steps has a different problem which I'll file separately...aryeh-looker commentedon Aug 1, 2022
Any tips/workarounds here?
alexeagle commentedon Aug 1, 2022
aryeh (@aryeh-looker) if you're asking about using TypeScript with Bazel, you might want to look at https://github.com/aspect-build/rules_ts which no longer needs any
rootDirssettings.