Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Project references in TypeScript 3 with separate `outDir`

Tags:

typescript

I'd like to make use of the project references features in TypeScript 3.1. The directory structure of my project originally looks like this after the compilation:

.
├── A
│   ├── a.ts
│   ├── dist
│   │   ├── A
│   │   │   └── a.js
│   │   └── Shared
│   │       └── shared.js
│   └── tsconfig.json
└── Shared
    ├── dist
    │   └── shared.js
    ├── shared.ts
    └── tsconfig.json

Contents of the Shared directory:

shared.ts:

export const name = "name";

tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",                          
    "module": "commonjs",                     
    "outDir": "dist",                        
    "strict": true
  }
}

Contents of the A directory:

a.ts:

import { name } from "../Shared/shared"; 

console.log(name);

tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "outDir": "dist",
    "strict": true
  }
}

And I could successfully run it by running node dist/A/a.js in the A directory.

What I didn't like about this was that everything was getting copied into A's output directory. I thought project references are here to fix this problem.

To enable project references, I added the following line to Shared/tsconfig.json.

"composite": true

And the following to A/tsconfig.json:

"references": [
  { "path": "../Shared" }
]

Now when I compile, the directory structure is like the following, which is what I expected:

.
├── A
│   ├── a.ts
│   ├── dist
│   │   └── a.js
│   └── tsconfig.json
└── Shared
    ├── dist
    │   ├── shared.d.ts
    │   └── shared.js
    ├── shared.ts
    └── tsconfig.json

However, when I run the node dist/a.js in the A directory, I get the following Error:

module.js:538
    throw err;
    ^

Error: Cannot find module '../Shared/shared'

The reason is that in the generated a.js file, the reference to the imported module is not resolved properly:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var shared_1 = require("../Shared/shared");
console.log(shared_1.name);

Is there a way to get it working without putting all output files into the same directory?

Or, is there a better way to organize my project to make use of project references?

like image 711
Wickoo Avatar asked Oct 09 '18 08:10

Wickoo


People also ask

What Is A TypeScript project reference?

Project references are a new feature in TypeScript 3.0 that allow you to structure your TypeScript programs into smaller pieces. By doing this, you can greatly improve build times, enforce logical separation between components, and organize your code in new and better ways.

What is include and exclude in Tsconfig?

The include and exclude properties take a list of glob-like file patterns. The supported glob wildcards are: * matches zero or more characters (excluding directory separators) ?

What is Tsconfig JSON for?

The tsconfig.json file specifies the root files and the compiler options required to compile the project. JavaScript projects can use a jsconfig.json file instead, which acts almost the same but has some JavaScript-related compiler flags enabled by default.

What is include in Tsconfig?

Include – It is a property that allows you to include a list of TypeScript files using the glob wildcards pattern. “include”: [ “src/**/*” ] Exclude – It is a property that allows you to exclude a list of TypeScript files using the glob wildcards pattern.


1 Answers

Indeed, getting relative import paths to work for both your source files and your output files is a pain. The official recommendation, if I understand it correctly, is to set up a master output directory for the entire composite project and have Shared and A subdirectories inside it, so the source files and output files can have the same relative layout. I don't know if that's what you meant you didn't want to do.

The only other option I know of is to have each component import the other components (in your case, you just have A importing Shared) using non-relative imports that point to output files (i.e., A/a.ts would import a path like Shared/dist/shared). Since you are using non-relative imports, the same import path appearing in either a source file or the corresponding output file resolves to the same target output file. Imports will not resolve in your IDE until you've built the composite project: this is a known limitation. Since tsc doesn't rewrite imports, you'll need to set up your runtime environment and/or use a bundler to handle the non-relative imports and, if necessary, set the baseUrl and paths TypeScript compiler options to match.

I'll be happy to help you through the details of either approach if needed; just let me know where you get stuck.

like image 129
Matt McCutchen Avatar answered Oct 18 '22 14:10

Matt McCutchen