NB: A public git repo for this question is at https://github.com/matthewadams/ts-test if you want to have a look at the full project.
I'm trying to separate main source compilation from test source compilation in my project. Here's the source directory structure:
src
main
...typescript source files and possibly child directories with sources
test
unit
...unit test sources
integration
...integration test sources
My goal is to compile only main sources into lib/main
, and only test sources into lib/test
.
I'm trying to put all compiler options that should be common to both main & test compilations in tsconfig.json
, then use command-line args to tsc
to provide the options that are specific to each compilation. Here's my current tsconfig.json
:
{
"compilerOptions": {
"strict": true,
"alwaysStrict": true,
"diagnostics": true,
"disableSizeLimit": true,
"esModuleInterop": true,
"extendedDiagnostics": true,
"forceConsistentCasingInFileNames": true,
"inlineSourceMap": true,
"inlineSources": true,
"listEmittedFiles": true,
"listFiles": true,
"module": "commonjs",
"pretty": true,
"target": "es2015"
}
}
A snippet from package.json
scripts
section is the following (I reject Gulp, Grunt & the like on the grounds of complexity, knowingly sacrificing portability):
"scripts": {
"transpile-main": "rm -rf lib/main && tsc --outDir lib/main --rootDir src/main -d --declarationDir lib/main",
"transpile-test": "rm -rf lib/test && tsc --outDir lib/test --rootDir src/test --typeRoots lib/main",
...other scripts here...
}
I can compile main sources without an issue, and they appear in lib/main
correctly. However, when I compile test sources, I get the following error:
$ npm run transpile-test
> @matthewadams/ts-test@0.1.0-pre.0 transpile-test /Users/matthewadams/dev/me/ts-test
> rm -rf lib/test && tsc --outDir lib/test --rootDir src/test --typeRoots src/main
error TS6059: File '/Users/matthewadams/dev/me/ts-test/src/main/Nameable.ts' is not under 'rootDir' 'src/test'. 'rootDir' is expected to contain all source files.
error TS6059: File '/Users/matthewadams/dev/me/ts-test/src/main/Person.ts' is not under 'rootDir' 'src/test'. 'rootDir' is expected to contain all source files.
error TS6059: File '/Users/matthewadams/dev/me/ts-test/src/main/PersonImpl.ts' is not under 'rootDir' 'src/test'. 'rootDir' is expected to contain all source files.
The thing that's confusing me is the message 'rootDir' is expected to contain all source files.
I'm trying to compile test sources against what's in lib/main
. I don't want all sources under one directory.
What is the correct combination of tsconfig.json
options & tsc
cli options to achieve my goal of separate main & test compilations?
rootDir
is expected to be the root of all source/input files. TS compiler proceeds like this:
collect all input files ("files" / "include" / "exclude" / imports)
--> for each included input file
--> chop off the "rootDir" from the input
--> prepend the "outDir"
The src/test
files import from src/main
, so currently rootDir
can only be set to src
or above.
Project references can solve all of the issues you described:
rootDir
will be set to src/main
or src/test
respectivelysrc
folder does not appear as part of the lib
outputsrc/main
cannot import src/test
(only vice versa should be possible)| tsconfig-base.json // other config options go here
| tsconfig.json // solution config containing references
|
+---lib // output folder
| +---main
| | mod.d.ts
| | mod.js
| | tsconfig.tsbuildinfo
| |
| \---test
| mod-test.js
\---src
+---main // referenced sub-project main
| mod.ts
| tsconfig.json
|
\---test // referenced sub-project test
mod-test.ts
tsconfig.json
./tsconfig-base.json:
{
"compilerOptions": {
// just some sample values, set your own as needed
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
// other compiler options
}
}
./tsconfig.json:
{
"files": [],
"references": [
{
"path": "./src/main"
},
{
"path": "./src/test"
}
]
}
./src/main/mod.ts:
export const foo = "foo";
./src/main/tsconfig.json:
{
"extends": "../../tsconfig-base.json",
"compilerOptions": {
"outDir": "../../lib/main",
"composite": true // flag to signal referenced project
}
}
./src/test/mod-test.ts:
import { foo } from "../main/mod";
// use your test framework here
export function testMod() {
if (foo !== "foo") throw new Error("foo expected.");
}
testMod()
./src/test/tsconfig.json:
{
"extends": "../../tsconfig-base.json",
"compilerOptions": {
"outDir": "../../lib/test"
},
"references": [
{
"path": "../main" // test project depends on main
}
]
}
You can build and clean the project with following scripts:
"scripts": {
"build": "tsc -b -v",
"clean": "tsc -b --clean"
},