TypeScript: tsc should not care and fail with ts files outside of rootDir
TypeScript: 2.0.0
I have a fixtures folder which contains .ts files. In my tsconfig.json I set rootDir to src which does contain all my source code (including test).
The fixtures files are there for test cases. tsc shouldn’t complain their existence and fail the compilation.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 73
- Comments: 90 (30 by maintainers)
the rootDir is used to build the output folder structure. all files have to be under rootDir for the compiler to know where to write the output. without this constraint, if there are files that are not under rootDir, the compiler has two options 1. either they will be emitted in a totally unexpected place, or 2. not emitted at all. i would say an error message is much better than a silent failure.
Bleh, this is really working as intended? I just ran into this, I told typescript that my source was all in a particular folder (via
rootDir) and then it proceeded to completely ignore what I told it because I happened to have a single file with a.tsextension sitting outside of that folder.This really feels like an error, if I tell the compiler where my source is via
rootDir, it shouldn’t completely disregard that setting and change my output folder structure because it thinks maybe I did something wrong. It should just ignore any files that are not inrootDir. Alternatively, it should hard fail, not continue to emit output that doesn’t align with my desired output folder structure at all!For anyone coming after me, the following will do what you actually want:
“tsc should not care and fail with ts files outside of rootDir” I agree.
I cannot understand why typescript would be looking at JS files outside the rootDir, since that’s where all the source should be. Error message says “‘rootDir’ is expected to contain all source files”, then anything outside that directory should not be expected or assumed to be a source file, as far as TS is concerned!
If this is “Working as Intended”, then what exactly is the intent or motivation of this behavior? Is there a case where TS would want to compile source files located outside the rootDir?
On the other hand, adding an “include” section in tsconfig solves the problem. When provided with files or directories to include, TS does not go looking at other files outside that.
IMO this comment should be especially taken into account to confirm that this is indeed a bug and should be re-opened as such:
Indeed, it does not make sense. “Source files outside
rootDir” simply are not source files, no matter their file extension or content. The compiler will never compile them. So why does he complain about their existence? Is he jealous that there are files not under his rule?+100 on this being a bug (a nasty one actually). Consider following directory structure:
I would like
tscto compile only thesrc, because I run tests withts-node. HavingrootDir: 'src'will cause compiling error currently. If you mark tests and other stuff that you only intend to execute withts-node(or even compile separately maybe?) as “excluded”, then bad things start to happen (e.g. vscode highlighting becomes broken in tests)In fact, no combination of
rootDir,excludedand all other options satisfies all requirements (exclude tests from compilation while still being able to run them and to work with them).And I don’t really think my use case is unique, so it’s worth reconsidering the “work as intended” label at least from UX point of view.
Had this as well. Typescript is too constraining for our team - considering flow now.
This is not what
rootDirmeans!rootDirmeans “Use this folder as the relative basis for computing the paths inoutDir”. Which files are part of your compilation is a separate question that is answered byfilesand/orinclude/excludeThe evil is in the naming. It really means
relativePathBaseDiror something like that.Just to add another example of the problem, this is my folder sturcture:
I have in my tsconfig:
And in my “index.ts” (src folder)
I know that, after the build, the “index.js” file will be inside the “lib” folder. And I know that the package.json will be 1 level up. I don’t think that Typescript should know better than me my folder structure and break the build. It should simply log a warning about some files outside the rootDir folder that weren’t included in the output folder.
@HillTravis you can trust me on this one 😃 it is our inconsistent handling of symlinks that throws the compiler off. we follow symlinks only for node module resolution, then use that name as the file name; then the we perform a check to make sure all files are under the source directory using the real path, instead of the user specified path. and that is what generates the error.
Here is my tsconfig.json:
My folder structure looks like this:
I want my .ts files in the src folder to be compiled and output with the same folder structures to the build folder. I do this so that I can set my document root to the build folder and keep my src out of the doc root. I’m getting error messages about .ts files in the node_modules folder, same error messages as OP. Note above, I have node_modules excluded. Why is tsc complaining about those?
I agree with @CodySchrank . I’m ok with having TS erroring out when some source file inside the rootDir references/imports a file outside of it - that is to be expected.
However, I can’t understand TypeScript to by itself look through unrelated files outside of rootDir and erroring out while those are simply configuration files for different tools, like jest or whatever. I mean, what’s the point? These are not even parts of my application and not referenced/imported anywhere directly, and my God, why would I event want them to be bundled into the resulting build in the first place…
To get around, I add “exclude” back to my
tsconfig.json@ngryman I agree that the definition of
rootDirand what it’s actually doing do not seem to line up. When you create a new ts project (viatsc --init) the comment intsconfig.jsonforrootDiris similar to what you quoted/* Specify the root directory of input files. Use to control the output directory structure with --outDir. */. If I supply a directory of input files, I expect the only ones that it is even going to consider compiling are in that directory, by the definition of input.@RyanCavanaugh It may have made sense to report an error like this 4 years ago when typescript was only used to be compiled into javascript. Of course I don’t want missing files! But now with the popularity of typescript execution, like
ts-node, it seems like tsc is being a little jealous to me.And the idea that typescript files outside of the
rootDirimplies an incorrect project configuration is simply wrong. I don’t need my unit tests, which are written in typescript, to be compiled to javascript.ts-nodeis simply faster and I get to enjoy all the benefits of typescript when writing tests.The implication of a file outside the
rootDirwould be that TS would output a file outside theoutDir, which I think is a self-evidently bad behavior.The error message could be better - I’d love a suggestion or a PR for that.
The docs could always be better - again, would love concrete ideas there on how to communicate things more clearly.
Insinuating that I’m calling people stupid isn’t something I’d like any more of.
The
src/test/outsetup was a key scenario addressed by project referencesAs a test, I tried removing the symlink. Actually, I deleted the whole
buildfolder. Then I rantscagain, and still saw the error.During more testing, I tried commenting out the in-code import and uses of that particular node module (ng2-datetime) and the error went away. So it’s only when I attempt to import that node module that I see the error, regardless of symlink.
If you look at the source for that package on GitHub, you’ll see that the package.json file has the “main” parameter set to “ng2-datetime.js”, although the author also published the .ts file with the same name. This seems to be causing the issue. If I create .d.ts files and delete the .ts files, the issue goes away. It compiled with no issues even after adding the symlink back.
So with all respect, I don’t see how this issue could be related to the symlink specifically. It seems to just generally cause issues when the author includes the .ts files in the package.
Do you know if there is any workaround for this? The OP above said the worked around it by adding “exclude” back to the tsconfig.json. I already have the directory excluded, but it’s still trying to compile it.
@HillTravis your issue seems to be the same as https://github.com/Microsoft/TypeScript/issues/9552.
That’s true. I futzed about with
includeand found that it works better than I thought for this case, including with VSCode’s tooling.I would still like to have code that is validated on compile but not emitted on compile, which is something that
includedoesn’t cover. But that is a separate request that needn’t be discussed here.I’m confused by this. Do you consider
rootDiran opt-in enforcement check? It affects what eventually gets emitted so it is clearly not only an enforcement check.Some flags, like
target, drastically change what will be emitted – they’re not enforcement checks but compiler instructions.rootDirreads like, and is documented like, a compiler instruction.From what I’ve read of this thread so far there seems to be a broad consensus that a reference from a file inside of
rootDirto a file outside ofrootDirshould be an error. The aspect of the feature which I and others have found frustrating is that it does not stop there, but in fact throws an error if it finds any TS files in the project folder outside ofrootDirwhether a file inrootDirimports them or not.It bears repeating: I set
rootDirto the folder where my source code resides, the compiler notices other TS code in the project that has nothing to do with what’s inrootDir, and it halts! This specific behaviour does not help me understand my code. It is simply annoying.It is true that from the perspective of the TypeScript definition of
rootDir, as elaborated in this thread, the behaviour is perfectly reasonable, and everyone who has come here to express their frustration about it is in error. But it is not reasonable from the conventional, expected definition of a root directory. I put it to you that in virtually any context where I provide a root directory, I am telling the program where to begin traversing the file structure, not asking it if it’s absolutely sure there isn’t something else in the filesystem I might also be interested in.Definitions can be wrong. They can be changed. For backwards compatibility, it is not necessary to retain definitions, only functionality.
I… don’t think I want that, for pretty much the same reasons that I want the compiler to throw an error when something in
rootDirimports code from outside of it. I do think the contents of arootDirif I provide one should be encapsulated from the surrounding code.I think the simplest way to avoid the behaviour I and others do not want is to implicitly exclude files outside of
rootDirif the files inrootDirdon’t refer to them. If the files inrootDirdo refer to them, an error should be thrown, as it is currently.I have also noticed that
includenormally means “look here as well” but in this case it means “only look here”. I would be sensible for it to have another name and be mandatory.guys, any resolution on this now? using types from client code inside backend code and otherwise.
what’s the best option? copy-paste there and there and modify if I change types?
making one root dir for 2 projects is not an option, since why backend tsconfig.json will have info on jsx and compile into commonjs when I can use es2015
@iwllyu can you check your project on
typescript@2.1.4and file a new issue if you are still having trouble.@mhegazy You labeled this issue with “Working as Intended” but thats not true. As you can see in my screenshot, tsc-server provides files outside of my rootDir (in this case mobiservice/src) as import suggestion. To me this looks like a bug…
This problem is caused exactly because the user is implicitly trying to compile files outside of the
rootDir. You basically told the compiler, “only compile files in this directory” and then you proceeded to reference files outside of that directory. The compiler is confused and doesn’t know how to proceed./project/source/index.ts/project/tsconfig.jsonIn this example, you tell the compiler “source files are only in the ‘source’ directory”. Then one of those files references a file that is not in the source directory. TypeScript now has two conflicting instructions, one says to only compile files in the source directory, the other says to compile files outside of the source directory.
I’m still very unclear about why the compiler wouldn’t just “chroot” to
rootDirand don’t consider files outside of that directory. As a user, probably lacking context abouttscinternals and history, this is definitely not a predictable behavior to me.One example that I can give out of my head is Jest which offers the same option: https://jestjs.io/docs/en/configuration#rootdir-string. AFAIK Jest doesn’t consider test files outside of
rootDir. I would expect the same behavior here for example.I’m glad that you are considering improvements here. The suggestion of @MicahZoltu could be interesting to explore.
On my end, I can restate that I don’t think
rootDiris predictable (hence people reaction), but as I understand it, changing the behavior of that option is not something to be considered. So as a compromise one thing I could also propose would be to renamerootDirto a more meaningful name,outBaseDircould be a candidate. But there is probably a be a better name.I did not insinuate anything and I’m sorry if you took it that way. I only stated a very simple fact: when users complain about something they don’t understand, there are effectively only 2 paths to take. I concluded that I hope you were not leaning to the first one. The fact that you associate yourself with one of these paths is honestly left to you.
I think I’ve said what I had to say, especially on giving candid feedback about how this thread was handled. So I’ll leave room for others to propose solutions.
@ngryman What next step do we have?
rootDirhas a definition - this is the common root of my source files. When a file isn’t in that common root, by the very definition ofrootDir, that is an error. That definition is documented. I can’t do anything about the fact that people have invented their own definitions forrootDirand are subsequently surprised.This is like going to Starbucks and saying that asking for a latte shouldn’t result in getting a mixture of espresso and steamed milk. It’s unactionable because you’re arguing with a definition. If you want some different drink, order something else. If Starbucks isn’t offering the kind of drink you want, make a suggestion for what that drink should be and what it should be made out of.
@mhegazy This issue was closed 3 years ago. However it seems that it’s still a source of confusion 3 years later.
Could you please revisit this “Working as Intended” status? Given the reaction of the community in this thread, it’s pretty safe to say that it is not behaving as the community expects it to behave. Whether it’s a documentation issue, renaming that option, changing its behavior, or adding a new option, I believe there is actually an action item on your side to be determined.
I can understand that you have way higher priorities than this one, but keeping this issue closed without any sign that the feedback given here has ever been valued doesn’t really convey a very good image for Typescript to be honest.
This is way too complicated. I have a monorepo and I can’t seem to find a way to properly compile each package for itself, emitting the correct directory structure, while importing local packages (from the same monorepo) using the
pathsoption.Either the directory structure in the output directory contains unwanted parent folders or tsc complains about files being outside the root directory.
Can’t be this complicated right?
I landed here because my assets folder contains an “MPEG Transport Stream” video file with extension
.tsso it made tsc crash even if the folder wasn’t in myrootDirpath 😄I fixed it by adding the assets folder in the
excludepaths but it was a disturbing behaviour 🤔Even if I accept that, the current behavior is still in error IMO. If I have any TS files outside of the specified directory then
rootDirwill not be the basis for computing the paths inoutDir, meaning the configuration I specified will be completely ignored.I think hard failing would be much better because the compiler effectively thinks I have given an invalid configuration. Ignoring configuration when it is invalid is (IMO) not the right way to handle this class of failure.
Example error message that would be far more clear (along with hard failure):
Note: A big part of the reason the current behavior of rootDir feels very wrong is precisely because it doesn’t make sense to have files outside rootDir. When one asks oneself (or the compiler) what does it mean to compile files outside
rootDirthe only answer is “it doesn’t make sense”. Therefore one comes to the natural conclusion that rootDir specifies sourceDir as well.with typescript 1.8.10
I had a node_modules folder in my homedir
~/node_modules. Runningtscfrom my project dir~/projects/myProject/caused typescript to complain about the following filesvery confusing - although not sure why I had a node modules folder in my home dir to begin with.
@SephReed: I’ve spent 200 rep points on StackOverflow and someone contributed this very simple solution for importing
../package.json. All you have to do is place atypings.d.tsfile in the same directory aspackage.json, with this contents:I have no idea how this works, but honestly, I don’t care. At that stage, TS was forking for itself, not for me.
@sebelga I’d probably consider that a bug - allowing
.jsonfrom outsiderootDirwould be OK since TS doesn’t copy that to the output folderIronically, what’s confusing is that they don’t interact: The settings do not affect each other at all.
includeis 100% in control of what’s in the program androotDiris 100% in control of computing the layout ofoutDir.I’ve had issues with this, as well over in https://github.com/microsoft/TypeScript/issues/31757#event-2480427393
The interplay between rootDir, include and exclude is just designed so weirdly.
I always thought that rootDir is the folder that the compiler starts traversing and include are additional folders (e.g. vendor folders) that might be referenced from rootDir.
The current definition is weird as hell and extremely unintuitive.
@RyanCavanaugh Thanks for your quick answer.
If I go to this page: https://www.typescriptlang.org/docs/handbook/compiler-options.html. I read the following definition:
I’m literally understanding that it’s “only used to control the output directory structure” and that’s it. The “only use” is important here. How could I even guess that it’s not the only thing that the compiler is doing in reality, but that it will also emit errors if I have Typescript files outside of that directory.
Perhaps I’m missing something, but if that’s the case, I’m far from being the only one. So if you have a product, and a good amount of your users don’t understand your product, there are only 2 paths to take: either you consider your users stupid, or either you failed to communicate something clearly. I hope you’re not leaning toward the first 😕
To finish with your Starbuck example, well write “mixture of expresso & steamed milk” in the ingredients list, so I know what a latte is, and I don’t have to guess.
@kpturner That sounds super reasonable, but — a barebones test project with no cross-folder imports will produce the behavior everyone is complaining about.
I’m guessing this is never going to be fixed. Just ran into the same issue. How this “works as intended” is beyond me. I guess the intention was to make the compiler run in a nonsensical manner.
I found this issue because I was googling why typescript was trying to compile my webpack.config.js file after I set
rootDir.This is the answer! The docs for rootDir say
Which I took to mean “input source files”. I recommend that @RyanCavanaugh’s comment replace the current explanation for this option.
@mhegazy I’m not sure it’s the same. Yes, my structure includes a symlink, but anyplace where that symlink exists should be ignored.
In #9552, there is no src folder, meaning the node_modules folder exists in the path that is being built. In my scenario, the root directory that tsc should be looking at is src, which does not contain node_modules. So it shouldn’t be looking at that folder at all. On top of that, I have the node_modules folder explicitly excluded via my tsconfig.json. So it should be doubly ignored. I also have the
buildfolder excluded, and that should cover the symlink to node_modules inside it.Also, in #9552, there was no mention of the error messages or failed compilation.
I should also mention that I’m using TypeScript 1.8.10.
Specifically, the error messages read:
@mhegazy Thanks for clarification. By name
rootDirI took it as the root dir of source code, much likebaseURLfor browser. Didn’t know it is only for output folder structure.@alexeypetrushin you can take a look at some of my projects. It seems to be working fine. e.g.:
I’m not that good at maintaining my composure when I see issues that have been open since 2016 that could probably be very fixed without introducing new issues for existing users by introducing a new “compilerOption” to let the compiler know it can ignore the error in a given use case.
For this, I apologize. I can’t help it, because I’m relied upon to provide a good service, yet I occasionally encounter issue threads like these. Why is it so difficult for the authors of TypeScript to implement one simple configuration flag?
I don’t think it’s because the developers are lazy, or because they’re unwilling to help. I don’t think anyone here is a bad person, except for myself.
It’s this conflict, this debate, which has gone on for years over what appears to be the definition of the property “rootDir”. And what to do about this issue.
People get thrown into this debate because they believe that they are right. Yes, “rootDir” serves as the base directory for the source code. Yes, the error is valid.
Yet, the underlying issue still persists. My tooling compiles the code perfectly, yet my IDE displays this error on my screen. It’s a perversion. I want those red lines gone. My files don’t have errors in them. And if they do, I should focus on those rather than being distracted by these non-issues.
All I want is to suppress this error message. I think that’s what most of us want. I don’t know if there are other cases here and maybe there are people who would like to speak up about that.
Can we please work together and solve this problem? It would make me so happy to see those red lines gone…
@RyanCavanaugh But it is what
rootDirshould mean, and this is what this issue is about.@RyanCavanaugh I can’t set it to “src” as mentioned above. I am importing “package.json” outside our src (
import pkg from '../package.json';) and tsc does not allow that.Thanks for sharing this @dylang ! I will try it.
@RyanCavanaugh I have a similar use case to @sebelga where I want to
console.logout the version number frompackage.jsonat runtime.This requires at the least a separate project config for each root folder (https://www.typescriptlang.org/docs/handbook/project-references.html). Having read through the instructions, I came away without a good understanding of how to implement the pattern and I feel like I’m being handed a jackhammer when what I requested was a flyswatter.
Sure, maintaining adjunct modules as separate sub-projects within my source folder is more efficient, but I’m not compiling lodash here. What I want is to include many files, like test and benchmark files, under a common project folder for static analysis, and when it comes time to compile, only emit a single directory for use by the outside world. This seems easy enough to specify and I’m struggling to see why it is not only difficult for the user to do, but apparently verboten from the perspective of the TS spec.
What I want to emphasize is that the only reason this feature is disallowed is due to the compiler’s assumption of corrupt intent on the part of the developer. When the developer sets the
"outDir"to./srcand the compiler finds a TS file in./test, it could assume that this file does not need to be emitted (assuming of course that it’s not referenced from any file in “outDir”).But it doesn’t. It assumes that the developer wanted to emit this file, and by incompetence or malice specified a subdirectory that does not contain it. In other words, the compiler has the opportunity to match the developer’s instructions to a common and reasonable use case, and instead matches it to something absurd and refuses to proceed.
As it stands, I don’t even see what the legitimate use case for
"outDir"is. I’m assuming it’s to support a project structure where thetsconfig.jsonis at the top level along with other configuration and metadata files, and the owner wants the compiled folder to represent only the source folder that’s nested inside it. (Just make sure none of those meta-files end with.ts: that would be absurd.)It seems to me that the design of the spec is user-antagonistic. There was a reference made to ordering a drink at Starbucks and being upset when it wasn’t what you expected, despite the ingredients being listed on the menu. This is apt as far as it goes, but I would liken this feature more to a hot dog stand that does not sell hot dogs.
Sure, the irritated customers should have gone round back and saw the big sign boldly proclaiming that the hot dog stand does not sell hot dogs. But as the confusion accumulates, there might be an opportunity for owners of the hot dog stand to reflect on how they are communicating the capabilities of their product to customers.
As I mentioned in https://github.com/microsoft/TypeScript/issues/9858#issuecomment-370653478, I think the solution to this problem is simply better error messaging. Back when I ran into it, and eventually found this GitHub issue, my biggest frustration came from how much time I wasted trying to figure out the problem, and then thinking it was a compiler bug, and then creating a repro case, and then finding a duplicate bug that says “working as intended”. Failing fast with a clear error message would have saved me all of that.
If there are source files outside of
rootDirthen it means the TypeScript project is configured incorrectly. Giving the user a clear message indicating this along with guidance on how to fix it I think would resolve the issue for a lot of people.No matter which options in whatever combination I use,
tscseems not to be able to handle a monorepo withpathsreferencing local packages in the same monorepo. I tried--build,--project,references,paths,extends,rootDir,rootDirs,include,exclude,baseUrl,outDirin all kind of combinations with different values and in the end it complains about files not being within therootDiror it screws up by emitting a wrong directory structure insideoutDir.Really? That I’ve never seen 😃
Excluded folders are always excluded in all my projects except where the circumstances to which I allude are present.
Are there bare bones examples on this thread? Difficult to tell from my phone.
From my own personal experience the reason a typescript build wanders off out of your
rootDirtree and tries to build stuff you don’t expect is the fact the you have (deliberately or inadvertently) directly or indirectly referenced something in that area from within yourrootDir. If you do that then the build will follow the reference tree regardless of whether or not you have tried to exclude the area containing the referenced file. Sometimes it can be a PITA trying to find out how/where you have done it - but invariably I find it is my own fault - not the fault oftsc. It is merely doing what I have told it to do.Whenever this hits me it is because of types. I import an interface or something that lives in a file that imports something else that lives in a file that imports something else…until eventually I end up importing something that lives in a file outside my
rootDirand very often in something I am trying to explicitly exclude in my config.HTH some of your get to the root of your pain. No amount of config will get you out of it - you just have to keep strict control of your import structure.
I am also facing this issue and have been unable to resolve it.
tsc compilation is walking the tree out of my included directory and into parent folders, causing duplicate type errors.
I have a nested folder structure which looks like this:
Now when i try and run tsc from within the example directory, i get the following error:
As you can see by the error, tsc is walking the tree up and out of the example folder, and looking in the node_modules folder of the parent directory! What feels even worse is that i have tried explicitely ignoring the parent node_modules directory with little effect.
Here is the tsconfig of the example diretory:
How can i solve this issue? None of the above seems to work out for me