Repository navigation
The compilerOptions.outDir config is incorrectly resolved when in a shareable config #29172
Description
Activity
Path-based compiler options (
outDir,outFile,rootDir,include,files) are resolved from the config file they're found in - we thought this'd be more consistent when combining config files, especially when you have multiple configs within the same project, as paths always get resolved relative to the file they were written in (so you can safely write references to any path you want in a config file without worrying about if that config getsextend'd later on - its paths will continue to work).It would be horribly breaking to change this behavior now~
Reacted by Markus Johnsson, Gabriel and German JablonskiReacted by Richard Simko, Daniel Hitzel, Sergio Cinos, Andrej K, Frost He, Rijk van Zanten, Adrien Foulon, Doug, Scotty Weeks, Paul Wijhenke and 9 moreReacted by Matt, ravenscar, Kirill Groshkov, Alvis Tang, João Vieira, Shahar "Dawn" Or, Marvin Heilemann, Chris Blossom, Russell Dempsey, Anton Bessonov and 37 more- addedSuggestionAn idea for TypeScriptAn idea for TypeScriptNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.
on Jan 2, 2019 But this makes "extends" pretty useless. Without outDir you cannot use project references with extends. A The use case for extends is that I specify baseline options for the compiler but paths and references should be based on project settings. It should be possible overwrite paths or even individual options.
@myscope/tsc-config/tsconfig.base.json
{ "compilerOptions": { "moduleResolution": "node", "target": "ES2018", "newLine": "lf", "jsx": "react", "strict": true, "allowSyntheticDefaultImports": false }@myscope/my-project/tsconfig.json
{ "extends": "@myscope/tsc-config/tsconfig.base.json", "compilerOptions": { "moduleResolution": "node", "target": "ES2018", "newLine": "lf", "jsx": "react", "outDir": "lib", "strict": true, "allowSyntheticDefaultImports": true }Resolved config:
{ "compilerOptions": { "moduleResolution": "node", "target": "ES2018", "newLine": "lf", "jsx": "react", "outDir": "@myscope/my-project/lib", "strict": true, "allowSyntheticDefaultImports": true }This should be a non-breaking change.
Reacted by Matt, Russell Dempsey, PushPeekPop, Tormod Haugene, Makary Hryshkin, Yuri Nezdemkovski, Alexis Tyler, Sahar Beker, Chris Ackerman, Jon Lauridsen and 12 moreJust want to chime in, I'm really surprised these paths are resolving relatively. This really makes config extensions much less useful.
I really just want to set my
rootDirandoutDiracross all of my packages uniformly by extending a singular base configuration - there's no way to do that right now without definingrootDirandoutDirin every single one of my packages.Took me a hot minute to find my build files inside
node_modules/@myorg/shared-tsconfigs/dist... 😭Reacted by Russell Dempsey, Cecile Muller, PushPeekPop, orphaninstance, Inaki Anduaga, Makary Hryshkin, Yuri Nezdemkovski, Sahar Beker, Chris Ackerman, Jon Lauridsen and 16 moreReacted by Stillward, Chris Ackerman and Manzoor WaniReacted by StillwardYou're correct that this is a breaking change though. Everyone is setting their paths in shared configs with
../../prefixing them. You can imagine what would happen if you made these paths start resolving from their downstream consumer's roots.I think this is very confusing, I read the docs about extends and when I read:
All relative paths found in the configuration file will be resolved relative to the configuration file they originated in.
I took this at it's word that if I used a relative path such as
{ ... outDir: './src', ... }in the base configuration path would be resolved relative to the base configuration's path. The implication here is that non-relative paths are not relative to where they appear but rather the project wherever
tscis run, so I expect:{ ... outDir: 'src', ... }to be relevant to the project.
It seems however that
srcand./srcare both identical wrt extends which seems like a very unintuitive decision.Reacted by Russell Dempsey, Shiv Jha-Mathur, PushPeekPop, Patrick Ryan, bherbruck, Gabriele Tomberli, Andrej K, Adrien Foulon, Dipun Mistry, Alexis Faizeau and 2 moreSince changing this behavior would be a breaking change and is impossible to make backwards compatible, could we maybe create new keys that resolve relatively?
I vote
srcDir(similar tonuxt) anddistDir. From there deprecation notices can be added to users still usingrootDirandoutDir.I'm opposed to the idea of making them something explicit like
relativeOutDirandrelativeRootDirbecause I don't think the current behavior should be the default behavior - it's very confusing to anyone attempting to use extended configurationsThe current implementation is rather unfortunate as it results in surprising behaviour. In addition to
outDir,outFile,rootDir,include,exclude,fileswe now also havetsBuildInfoFile.Incidentally, in the MSBuild config inheritance implementation (Directory.build.props), the setting for
<OutputPath>mypath</OutputPath>is absolute. Hence can be conveniently defined at the solution level.There is a proposal for a non-breaking implementation in #30163
kirillgroshkov commented
on Apr 20, 2019 More actionsI agree, got confused about it as well. Any plans to change this behaviour to support relative paths in shareable configs?
Rather than a breaking fix, couldn't one just handle placeholder variables, such as $PROJECT_DIR or $ROOT_DIR? So the
outDirin my common config file could be"$PROJECT_DIR/lib"?Reacted by ExE Boss, Russell Dempsey, Jed, Anton Bessonov, Miroslav Bajtoš, Ritchie Zhu, Cecile Muller, Trukil, Filippo Conti, PushPeekPop and 61 moreReacted by desmapReacted by Nate Ryall, dmitrysteblyuk, desmap, Djobbo-Victor, william-will-angi and Evandro AraújoReacted by desmap, Fernando Pasik and Aldo D'AquinoReacted by desmapReacted by desmap and Anton GilgurIt's such a surprise that
extendsresolves path relatively to the source config rather than the inhering config.Consistency? YES definitely.
Use case? NO Don't think so.As a workaround, here is my hack:
- link the source
tsconfig.jsonto the same directory of target project e.g.
$ ln -s node_modules/<tsconfig_config_pkg>/tsconfig.json tsconfig.base.json
- then make a
tsconfig.jsonwhich extendstsconfig.base.jsone.g.
{ "extends": "./tsconfig.base.json"}Would be great if suggestion such as #30163 can be accepted!!!
Reacted by Russell Dempsey, PushPeekPop, Smith Samuel, Alexandre Costa and bittttttenReacted by Andrej K and Aldo D'Aquino- link the source
I ran into this while making pnpm use a shared config.
Reacted by Ryan Craig Martin, Dziamid, Rohit Garg, Gabriele Tomberli, Harry Talbot and Lior Sabag- added a commit that references this issue
on Jun 10, 2019 11 remaining items
- added a commit that references this issue
on Jun 6, 2022 - added a commit that references this issue
on Jun 14, 2022 As per Martin Doyle (@MartinDoyleUK) 's suggestion - which is similar to what Jest has done with
<rootDir>, please add a$ROOT_DIRoption to support this common use case.Reacted by Alexis Tyler, Daniel Hitzel, Ádám Kovács, Hadi, Nahuel Dallacamina Ortea, Brody McKee, Gabriele Tomberli, Greg Zapp, zanminkian, hardfist and 22 more- added a commit that references this issue
on Nov 5, 2022 4 years and we still can't get one of the non-breaking solutions merged?
Reacted by Lioness100, OMG, Emil Lindén, jaylenchen, Thiago Santaguida, HolyNoodle, Daolot, kumamaki, Vladimir Kuznichenkov, sosoba and 13 moreI just was making a new project in TS (pretty much the first time I'm doing a monorepo TS project from the scratch) and I also faced this issue. I was expecting paths to be resolved from the "final"
tsconfig.jsonfile.None of workarounds menitoned during the discussion works. I also have a feeling that adding root directory variable would be a viable solution to this - though discovery of it wouldn't be perfect.
The only solution for me is to use:
{ "extends": "../tsconfig-package.json", "compilerOptions": { "outDir": "./dist", // Needed due to https://github.com/microsoft/TypeScript/issues/29172. } }Which is not ideal but still makes few lines to be reused.
Reacted by zanminkian and Rasmusfabiospampinato commented
on Aug 28, 2023 More actionsNone of workarounds menitoned during the discussion works.
My workaround works and I use it daily. You need to install a dependency that exports a
tsconfig.json, extend that in yourtsconfig.json, and have the dependency automatically rewrite itstsconfig.jsonto point to the right paths absolutely.It's weird, but that works, unless you are using a package manger that isn't really installing things but just symlinking them or something.
I faced this issue. Assuming I have a monorepo.
. ├── packages │ ├── my-pkg1 │ │ ├── src │ │ ├── package.json │ │ └── tsconfig.build.json │ └── my-pkg2 │ ├── src │ ├── package.json │ └── tsconfig.build.json ├── package.json └── tsconfig.jsonEvery
tsconfig.build.jsonextends thetsconfig.jsonin the project root.
I have to write redundant configs in eachtsconfig.build.jsonfile:{ "extends": "../../tsconfig", "include": ["src"], "exclude": ["**/*.spec.ts"], "compilerOptions": { "outDir": "dist" } }It's redundant and ugly.
This is honestly crazy. about five years later and we still don't have anything.
even just an extra config property we could put in the base/shared config like
outDirBaseUrlOverrideorallowExtendOutDirwould be a good simple fix which i cant imagine being a breaking change as its something that needs to be manually enabledReacted by Max, Andrey Grebenyk, Wyatt Stanke, Marek Stasikowski, Doug, Geraldo Neto, DaveCousineau, Patrick "PJ" Conroy and YihengReacted by Ryan CavanaughRyanCavanaugh commented
on Feb 16, 2024 MemberMore actionsWe're discussing options about this at #56436
Reacted by Kirill Groshkov, Anton Bessonov, Chris Haws, Simon Lagos, Liam Jones and Jimmy HoranWow, great to know a solution has been implemented and merged!
As far as I understand it, soon we should be able to write a base config such as:
// @fileName: tsconfig.base.json { "compilerOptions": { "rootDir": "${configDir}/src", "outDir": "${configDir}/lib", "tsBuildInfoFile": "${configDir}/lib/.tsbuildinfo", } }
Reacted by Ryan Cavanaugh, Zephyr Lykos, Takuya Fukuju, Johannes Schickling, silverwind, Ivan Polushin, Viki, Sergey Makarov, François Best, Rich Cooper and 10 more- added a commit that references this issue
on Apr 20, 2024
TypeScript Version: 3.3.0-dev.20181222
Search Terms:
outDir,output directory,outDir extendsExpected behavior:
TypeScript 3.2 got support for configuration inheritance via node_modules packages. I have created a package with a shareable config. In this shareable config, I have defined the
outDiroption: https://github.com/sindresorhus/tsconfig/blob/50b0ba611480ed45b97a59b910f5bb5e8cbc25ef/tsconfig.json#L2-L3 as I always use the sameoutDirand don't want to have to specify it in each project.I expected the
outDirpath to be resolved against the project root, even when it's defined in an extended config.Actual behavior:
It turns out the
outDirrelative path is actually resolved against the shareable config path instead of the project's root (tsconfig.json) path. So when I have a projectfoo, and compile TypeScript, the output ends up infoo/@sindresorhus/tsconfig/distinstead offoo/dist.You can reproduce it by cloning https://github.com/sindresorhus/ow/tree/8ae048c4931dfd51b496cefb40b24c78d3722be6, then removing this line https://github.com/sindresorhus/ow/blob/8ae048c4931dfd51b496cefb40b24c78d3722be6/tsconfig.json#L4 (which is a workaround to the problem), and then run
$ npm test. The compiled TS code will end up innode_modules/@sindresorhus/tsconfig/distinstead ofdist.