Closed sindresorhus closed 6 months ago
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 gets extend
'd later on - its paths will continue to work).
It would be horribly breaking to change this behavior now~
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.
Just 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 rootDir
and outDir
across all of my packages uniformly by extending a singular base configuration - there's no way to do that right now without defining rootDir
and outDir
in every single one of my packages.
Took me a hot minute to find my build files inside node_modules/@myorg/shared-tsconfigs/dist
... π
You'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 tsc
is run, so I expect:
{
...
outDir: 'src',
...
}
to be relevant to the project.
It seems however that src
and ./src
are both identical wrt extends which seems like a very unintuitive decision.
Since 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 to nuxt
) and distDir
. From there deprecation notices can be added to users still using rootDir
and outDir
.
I'm opposed to the idea of making them something explicit like relativeOutDir
and relativeRootDir
because I don't think the current behavior should be the default behavior - it's very confusing to anyone attempting to use extended configurations
The current implementation is rather unfortunate as it results in surprising behaviour. In addition to outDir
, outFile
, rootDir
, include
, exclude
, files
we now also have tsBuildInfoFile
.
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
I 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 outDir
in my common config file could be "$PROJECT_DIR/lib"
?
It's such a surprise that extends
resolves 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:
tsconfig.json
to the same directory of target project e.g.
$ ln -s node_modules/<tsconfig_config_pkg>/tsconfig.json tsconfig.base.json
tsconfig.json
which extendstsconfig.base.json
e.g.
{ "extends": "./tsconfig.base.json"}
Would be great if suggestion such as #30163 can be accepted!!!
@weswigham what's about this comment from @MartinDoyleUK ?
Would love to see this solution.
Not sure if related or different bug / feature, but I have (v 3.6.3):
"baseUrl":"./src",
"paths": {
"common/*": ["../../common/src/*"]
}
If I use the path in imports:
import logger from 'common/logger';
generated files will look like:
NOTE there's a copy of logger in server src
server/dist
βββ common
βΒ Β βββ src
βΒ Β βββ logger.js
βΒ Β βββ logger.js.map
βββ server
βββ src
βββ csp.js
βββ csp.js.map
βββ index.js
βββ index.js.map
βββ logger.js
βββ logger.js.map
without the import using the path config (but keeping the path config)
import logger from './logger';
server/dist
βββ csp.js
βββ csp.js.map
βββ index.js
βββ index.js.map
βββ logger.js
βββ logger.js.map
Full tsconfig
{
"compilerOptions": {
"module": "commonjs",
"declaration": false,
"noImplicitAny": false,
"removeComments": true,
"noLib": false,
"allowSyntheticDefaultImports": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es6",
"sourceMap": true,
"allowJs": true,
"outDir": "./dist",
"skipLibCheck": true,
"baseUrl":"./src",
"paths": {
"common/*": ["../../common/src/*"]
}
},
"exclude": [
"node_modules"
],
"include": [
"src/**/*"
]
}
build command
tsc -p tsconfig.json
Just define "rootDir": "."
together with outDir
within your child module, this fixed the issue for me. Or remove rootDir
from yoor root tsconfig.json
@weswigham any news on this?
I found @MartinDoyleUK's idea to intro $PROJECT_DIR
or $ROOT_DIR
vars quite smart and they aren't a breaking change.
Would it be possible to describe this behavior explicitly on https://www.typescriptlang.org/tsconfig#outDir ?
Just stumbled on this as well. This is so weird, the one thing I was sure extending another configuration would enable was using a single configuration for all the settings shared between projects. Like it's annoying to say to TS for every project to get the files from "src" and to put them in "dist".
Apparently there's a CLI option for setting the output directory, but not the input one, so even abstracting away the call to tsc
via some CLI app this doesn't seem achievable.
Edit: maybe I don't need to specify the "include" option at all though, TS seems smart enough to be able to understand where to read files from. I'll see if I can find some issues with this approach.
Edit 2: never mind, after the first build TS thinks that the files in the output directory are input files, so it isn't particularly smart really.
Found a "solution":
tsconfig.json
you want to share between your projects.tsc
to do its work.tsc
make the module rewrite it's own local tsconfig.json
with absolute paths to the folders that you want.A bit weird, but it should work for any kind of option that arguably TS implements weirdly, and it only has to be coded once, so good enough for me. This is the module I'm using, but it's really super tailor made for my workflow.
I would love to see this closed/fixed so it just works. After trying quite a few monorepo setups in typescript(I come from java-land where it was a much easier task), this monorepo setup below seemed the best but you run into this github issue.
As per @MartinDoyleUK 's suggestion - which is similar to what Jest has done with <rootDir>
, please add a $ROOT_DIR
option to support this common use case.
4 years and we still can't get one of the non-breaking solutions merged?
I 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.json
file.
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.
None 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 your tsconfig.json
, and have the dependency automatically rewrite its tsconfig.json
to 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.json
Every tsconfig.build.json
extends the tsconfig.json
in the project root.
I have to write redundant configs in each tsconfig.build.json
file:
{
"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 outDirBaseUrlOverride
or allowExtendOutDir
would be a good simple fix which i cant imagine being a breaking change as its something that needs to be manually enabled
Hello from 2024, this is still an issue.
Hello from 2024, this is still an issue.
Is typescript still maintained?
We're discussing options about this at #56436
Wow, great to know a solution has been implemented and merged!
https://github.com/microsoft/TypeScript/pull/58042
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",
}
}
TypeScript Version: 3.3.0-dev.20181222
Search Terms:
outDir
,output directory
,outDir extends
Expected 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
outDir
option: https://github.com/sindresorhus/tsconfig/blob/50b0ba611480ed45b97a59b910f5bb5e8cbc25ef/tsconfig.json#L2-L3 as I always use the sameoutDir
and don't want to have to specify it in each project.I expected the
outDir
path to be resolved against the project root, even when it's defined in an extended config.Actual behavior:
It turns out the
outDir
relative 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/dist
instead 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/dist
instead ofdist
.