M.K. Safi
M.K. Safi

Reputation: 7039

TypeScript `tsc` not picking up tsconfig.json inside a subdirectory?

I have the following directory structure:

.
├── tsconfig.json ("module": "CommonJS")
└── foo/
    ├── node-file.ts
    └── bar/
        ├── browser-file.ts
        └── tsconfig.json ("module": "esnext")

The root tsconfig.json has module set to CommonJS because I want most of my files to compile for Node. But inside bar, I want the files to compile to JavaScript Modules, so I've set module to esnext.

Now when I run tsc from the root, I expect node-file.ts to compile to CommonJS module and browser-file.ts to compile to a JavaScript Module. But that's not what I'm getting. It seems that tsc is completely ignoring foo/bar/tsconfig.json and is only picking up the root tsconfig.json.

(I also use tsc --watch while developing, so I'm trying to avoid having to run two different tsc processes to compile the two different targets. It feels to me that running a single tsc with nested tsconfig.json files should give me the desired results.)

Anyone know what it is that I'm doing wrong?

Upvotes: 22

Views: 21449

Answers (1)

Lauren Yim
Lauren Yim

Reputation: 14148

TypeScript only uses one tsconfig.json and doesn’t automatically use subdirectories’ tsconfig.json for the files there. However, you can use project references for this.

Create a directory structure like this:

.
├── tsconfig.json
├── tsconfig.settings.json (optional)
└── foo/
    ├── node-file.ts
    ├── tsconfig.json ("module": "commonjs")
    └── bar/
        ├── browser-file.ts
        └── tsconfig.json ("module": "esnext")

tsconfig.json

{
  "files": [],
  "references": [
    {"path": "./foo"},
    {"path": "./foo/bar"}
  ]
}

This is the root tsconfig.json. When you run tsc --build (see below) in the root directory, TypeScript will build the referenced projects ./foo/tsconfig.json and ./foo/bar/tsconfig.json.

The "files": [] is to stop accidental tscs without --build from attempting to compile everything in the root directory, which will error but create multiple .js files in possibly the incorrect places.

tsconfig.settings.json (optional)

{
  "compilerOptions": {
    "strict": true,
    "noImplicitReturns": true
  }
}

You can put configuration common to foo and foo/bar and extend this configuration with extends to reduce duplication. Note that all relative paths in here will be resolved relative to tsconfig.settings.json when extended, so something like "outDir": "dist" may not work as expected.

foo/tsconfig.json

{
  "extends": "../tsconfig.settings.json",
  "exclude": ["bar/**/*.ts"],
  "compilerOptions": {
    "module": "commonjs"
  }
}

This is the configuration for the CommonJS files. It also extends the common config and excludes the files in foo/bar.

foo/bar/tsconfig.json

{
  "extends": "../../tsconfig.settings.json",
  "compilerOptions": {
    "module": "esnext"
  }
}

This is pretty similar to foo’s configuration.


Building

To compile foo and foo/bar at the same time, use build mode from the root directory:

tsc --build         # or tsc -b
# Watch mode:
tsc --build --watch # or tsc -bw

From the handbook:

A long-awaited feature is smart incremental builds for TypeScript projects. In 3.0 you can use the --build flag with tsc. This is effectively a new entry point for tsc that behaves more like a build orchestrator than a simple compiler.

Running tsc --build (tsc -b for short) will do the following:

  • Find all referenced projects
  • Detect if they are up-to-date
  • Build out-of-date projects in the correct order

You can provide tsc -b with multiple config file paths (e.g. tsc -b src test). Just like tsc -p, specifying the config file name itself is unnecessary if it’s named tsconfig.json.

You can also compile individual projects:

tsc -b foo     # or cd foo     && tsc -b
tsc -b foo/bar # or cd foo/bar && tsc -b

Note that are some build-only flags and you cannot override compiler options with command-line arguments.

Upvotes: 30

Related Questions