11

My package looks like this:

┌ tsconfig.json
├ src/
│ ├ index.ts         (import './dependence.ts')
│ └ dependence.ts
└ example/
  ├ index.html
  └ script.ts        (import '../src/index.ts')

I would like

  • ./src/*.ts to be compiled in ./dist/*.js
  • ./example/*.ts to be compiled in ./example/*.js

after running tsc, I expect my package to look like this:

┌ tsconfig.json
├ src/
│ ├ index.ts         (import './dependence.ts')
│ └ dependence.ts
├!dist/
│ ├!index.js         (import './dependence.js')
│ └!dependence.js
└ example/
  ├ index.html
  ├ script.ts        (import '../src/index.ts')
  └!script.js        (import '../dist/index.js')

I'm a little confused about all tsconfig options.
I have tried many things with options like baseUrl, paths, rootDir, outDir, rootDirs, ... without success.

Inigo
  • 12,186
  • 5
  • 41
  • 70
Yukulélé
  • 15,644
  • 10
  • 70
  • 94

1 Answers1

13

Easy. Use separate tsconfig files, one in each source dir to establish each as a separate project and use Typescript project references to establish the dependency between the example project and the src project.

  1. src/tsconfig.json:
{
    "compilerOptions": {
       "rootDir": ".",
       "outDir": "../dist/",
       "composite": true  // required if another project has a reference to this one.
    },
} 
  1. example/tsconfig.json:
{
    "compilerOptions": {
      "rootDir": ".",
      "outDir": ".",  // because you want example to compile in place
    },
    "references": [   // declare the dependencies
      { "path": "../src" }  
    ]
}
  1. use tsc -b instead of tsc -p for incremental (i.e. faster) builds:

    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

    e.g.

    • tsc -b src to just build src
    • tsc -b example will build both because of the dependency
    • tsc -b src example if you feel like being explicit
  2. if you want to share some or most settings between projects to be DRY or to enforce consistency, you can:

    • put another tsconfig in the root and move all the common settings to it.
    • change each child config to extend the root config:
      "extends": "../tsconfig.json"
      
      adjusting the path if the child config is deeper in the tree.
  3. You can take step 4 one further and use it as "a 'solution' tsconfig.json", as described in this section of the Project References documentation. This should enable you to build everything from your overall project root with tsc -b .

I think I covered everything you need. There's a detailed explanation of outDir and project references in this answer. Let me know if I'm missing something or if you have any questions.

Inigo
  • 12,186
  • 5
  • 41
  • 70
  • Thanks! This work with the two `tsconfig.json` files but if I add the 3rd one in the root directory, root settings are ignored. Which command should I run? Should I add references between the root conf and the child confs? – Yukulélé Jul 04 '21 at 09:03
  • @Yukulélé, Root settings are not ignored. Settings closest to the code (i.e. in the same directory) will override any settings farther up the directory tree. So if you want common settings, you need to take out the settings in the non-root configs that you want to inherit from the root config. It's all in the docs. You should read them. – Inigo Jul 05 '21 at 02:16
  • References are only for projects that have a build dependency on another project. If you only use the root config for shared settings, then no, it doesn't need references. You *could* setup the root config to be a build target, with `src` and `example` as dependencies, and just say `tsc -b .` to build the root. But you really should understand how all this works before setting that up. Please read the docs. Otherwise keep it simple as I helped you with above. – Inigo Jul 05 '21 at 02:26
  • Yes, it's what i do: I use `"target": "es6"` in root `./tsconfig.json` and I no provide `target` setting in `src/tsconfig.json`. If I run `tsc -b .` build works but `./src/*.ts` are build into `./src/*.js` instead of `./dist/*.js`. If I run `tsc -b src example` build is done in right the `dist/` directory but `target` is not taken into account. – Yukulélé Jul 06 '21 at 08:44
  • 2
    @Yukulélé my bad! I forgot that `tsconfig` doesn't automatically inherit configs up the tree (like some other systems). I updated my answer... See #4. – Inigo Jul 06 '21 at 09:18
  • I'm pretty sure you can make `tsc -b .` work. See https://www.typescriptlang.org/docs/handbook/project-references.html#overall-structure about how to set up a "solution `tsconfig`". I don't do that myself, but mentioned it because I remembered reading this. ANYWAY, moral of the story is you probably should rean the TSCONFIG manual if you're going to do fancy stuff like this even if you get help from SO – Inigo Jul 06 '21 at 09:29
  • Thanks! this work with `tsc -b src example` but `tsc -b .` don't take `outDir` into account and generate files in place – Yukulélé Jul 06 '21 at 10:06
  • 1
    Did you read my second comment and follow the directions in the handbook? i.e. did you "set files to an empty array" in the root config. Did you set up the `composite` and `references` properties correctly (all three of your tsconfigs will need different combinations of these two). – Inigo Jul 06 '21 at 11:25
  • Yes It works ! thanks ! Can you update your answer to add root config an example with `composite`, `reference` and empty `files`? – Yukulélé Jul 07 '21 at 10:37