Skip to content

TypeScript 3 project references with a separate outDir #27572

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
afroozeh opened this issue Oct 5, 2018 · 13 comments
Closed

TypeScript 3 project references with a separate outDir #27572

afroozeh opened this issue Oct 5, 2018 · 13 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@afroozeh
Copy link

afroozeh commented Oct 5, 2018

TypeScript Version: 3.1.1

Search Terms: Project References

Code

I'm not sure if this issue is addressed somewhere else, at least I couldn't find an answer, so I'm asking again. I'm trying to use project references for a shared project with specified outDir.

The directory structure looks like this after the compilation:

.
├── A
│   ├── a.ts
│   ├── dist
│   │   └── a.js
│   └── tsconfig.json
└── Shared
    ├── dist
    │   ├── shared.d.ts
    │   └── 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,                           
    "composite": 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,
  },
  "references": [
    { "path": "../Shared" }
  ]
}

Compile the files by running tsc -p . in the Shared and A directories.

Expected behavior:
The application run successfully when running node dist/a.js in the A directory.

Actual behavior:

Files compile successfully, but when running 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?

@afroozeh afroozeh changed the title TypeScript 3 project references with separate outDir TypeScript 3 project references with a separate outDir Oct 5, 2018
@sheetalkamat sheetalkamat added the Needs Investigation This issue needs a team member to investigate its status. label Oct 5, 2018
@sheetalkamat sheetalkamat self-assigned this Oct 5, 2018
@DanielRosenwasser
Copy link
Member

This is actually unrelated to project references. When using outDir with project references, the only thing that they do is signal to TypeScript is to do a rebuild if the .d.ts files are out of date.

Ultimately this is just about how TypeScript doesn't touch import paths at all, and you need to import from wherever the .js and .d.ts files end up (rather than the input files).

@DanielRosenwasser DanielRosenwasser added Working as Intended The behavior described is the intended behavior; this is not a bug and removed Needs Investigation This issue needs a team member to investigate its status. labels Oct 5, 2018
@DanielRosenwasser
Copy link
Member

Also, see #16577.

@afroozeh
Copy link
Author

afroozeh commented Oct 6, 2018

I think I'm a bit confused about the project reference feature that is added in TypeScript 3.

If you look at my project structure, before using the project references, I had something like this:

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

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.

So, my questions is that how is the project references features is supposed to be used in case like mine?

  • Should I use just one output directory?
  • Should I manually rewrite generated output files to fix the import paths?
  • Is there another, better way that I organize my project to make use of project references?

Thanks in advance for your answer.

@afroozeh
Copy link
Author

afroozeh commented Oct 8, 2018

@DanielRosenwasser Also related to my previous comment, can you elaborate a bit more on the 'you need to import from wherever the .js and .d.ts files end up (rather than the input files)'? How should I change importing from the shared module when I have project references?

@stherrienaspnet
Copy link

I'm also really confuse about it, all the example i found are not working properly...

@stherrienaspnet
Copy link

Where could I found a working example of project reference feature?
Thanks :)

@stherrienaspnet
Copy link

@DanielRosenwasser can you elaborate a bit more on the 'you need to import from wherever the .js and .d.ts files end up (rather than the input files)'? How should I change importing from the shared module when I have project references?

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@lukka
Copy link
Member

lukka commented Nov 27, 2019

Anyone would expect that when you reference a project, you also reference the output created by that project. Otherwise what you reference it for? This is completely missing the point of Keep It Simple, and make using this technology fun, not a nightmare.

Really weird that this ticket is closed with no proper resolution. At least a link to a working solution should be explicitly pointed out.

@cyberalien
Copy link

cyberalien commented Jan 11, 2020

Combined with VSCode's automatic imports that link to source .ts file, this is a bad solution.

For example, in my project I have this structure:

├── src
│   ├── foo.ts
│   └── tsconfig.json
├── tests
├─── foo_test.ts
├─── tsconfig.json
└── tsconfig.json

Root tsconfig.json:

{
	"references": [
		{
			"path": "./src/tsconfig.json"
		},
		{
			"path": "./tests/tsconfig.json"
		}
	]
}

src/tsconfig.json:

{
	"extends": "../tsconfig-base.json",
	"compilerOptions": {
		"outDir": "../lib",
		"rootDir": "."
	}
}

tests/tsconfig.json:

{
	"extends": "../tsconfig-base.json",
	"compilerOptions": {
		"outDir": "../tests-compiled",
		"rootDir": "."
	}
}

If src/foo.ts exports type Foo, in tests/foo_test.ts I start typing this:
let foo: Foo
When typing Foo, VSCode suggests Foo type. So I click enter to confirm suggestion, VSCode automatically adds import to top of file, pointing to src/foo.ts

This makes VSCode inconvenient because after every automatic import I need to edit import to point to .js file (also make sure its compiled, so I can't edit two files at the same time). So one software has bug: TypeScript or VSCode. I prefer to think its TypeScript bug.


edit: I was wrong. By adding this to root tsconfig.json VSCode starts linking files from lib, so now it all makes sense:

	"files": [],
	"include": [],

@DanielRosenwasser
Copy link
Member

Also related to my previous comment, can you elaborate a bit more on the 'you need to import from wherever the .js and .d.ts files end up (rather than the input files)'?

To clarify, if you're in projectB/src/projBMain.ts, and you need to consume projectA/src/projAMain.ts, you need to import from wherever projAMain.js is going to be emitted to. So you'd need to import projectA/dist/projAMain.js in some way.

Now the actual problem you might run into is that if you use relative paths to projectA from projectB, your output directory in projectB will need to have the same level of nesting. In other words, if projectB/src/projBMain.ts is emitted to projectB/output/folder/very/deep/projBMain.js, then a relative import is going to break, and that's considered working as intended (or perhaps a design limitation).

I think in this situation, your best bet is something like path-mapping or symbolic linking (maybe like in this Lerna example).

@sdsmith
Copy link

sdsmith commented May 25, 2021

I want to mention that this is still a big use ability issue almost 3 years later. Having the structure of your project be implicitly limited in a way that most programming languages allow without any guide or explicit explanation is anti-user design.

Making the generated JS import location patched from the output location of the referenced project seems the most obvious solution at first glance. Or, at the very least, having an explicit warning about import locations if they are relative and not at the same nesting level with the reference projects output.

@otto-dev
Copy link

I have wasted so much time on this (in friendly terms) half-baked and misleading feature now, and no success.

The typescript compiler allows you to "compile" code that's impossible to run. What's the job of the compiler and static analysis then?

This is a big mess. You have to manually mirror your source and target directory structures. It becomes practically impossible as soon as your rootDir is not ., but something like ./src.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

9 participants