Skip to content

[Breaking Change] Typescript 3.7 converts reference paths to tsconfig specified paths breaking multi-project compiles. #36763

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

Open
Griffork opened this issue Feb 13, 2020 · 12 comments
Labels
Needs Investigation This issue needs a team member to investigate its status.
Milestone

Comments

@Griffork
Copy link

Typescript is replacing part of a relative path with one of the paths specified in tsconfig's path section. This is breaking the projects that rely on this file as they cannot resolve the path.

TypeScript Version: 3.7
Visual Studio Version: Community 2017

Search Terms:
Typescirpt paths tsconfig reference paths alias.

Code

In the client project at folder\folder\file.ts

/// <reference path="../../External/Knockout/index.d.ts" />

Becomes (in ClientDeclarations) folder\folder\file.d.ts:

/// <reference types="@Client/external/knockout/index" />

My tsconfig file (folder names with sensitive information are replaced with ***)

{
  "compileOnSave": true,
  "compilerOptions": {
    "module": "amd",
    "target": "es6",
    "lib": [ "es6", "dom" ],
    "sourceMap": true,
    "strict": true,
    "build": true,
    "declaration": true,
    "declarationDir": "../ClientDeclarations",
    "baseUrl": "./",
    "paths": {
      "@Common/*": [ "../Common/*" ],
      "@Server/*": [ "../ServerDeclarations/*" ],
      "@Client/*": [ "./*"]
    }
  },
  "include": [
    "./*",
    "./External/***/koco.ts",
    "./External/***/kocss.ts",
    "./External/***/kopath.ts"
  ],
  "exclude": [
    "node_modules"
  ],
  "references": [
    { "path": "../Common" }
  ]
}

Expected behavior:
The path should not be transformed during output.

Actual behavior:
The path is transformed to include an @Client reference, which my dependant projects can't resolve. This did not happen in tsc 3.4.

Additionally all of the casing is altered - meaning that this path would not resolve on linux machines.

Related Issues:
I haven't been able to find any - probably because what I'm trying to look for is called a "path" (in tsconfig) which is a highly overloaded term, making searching for anything related to it almost impossible. It would be nice to call it something like a "path alias" so that searching for related issues was easier.

@Griffork Griffork reopened this Feb 13, 2020
@Griffork
Copy link
Author

Griffork commented Feb 13, 2020

I think I found the root of the issue (from issue 5112):

The .d.ts files are considered "references" the compiler will not touch them, not move them, or recreate them. the only thing it will do, if you have a /// to an input.d.ts, the generated output will have the reference rewritten to point to the source location.

Typescript is now rewriting the paths to use the custom paths specified in tsconfig rather than a relative path breaking the project that includes the client. The project that includes client specifies @Client as relative the the declaration output directory (which doesn't have Knockout/index.d.ts because declaration files don't get copied to the output directory) whereas client specifies @Client as relative to it's own directory.

@Griffork Griffork changed the title [Breaking Change] Typescript 3.7 converts paths to tsconfig specified paths breaking multi-project compiles. [Breaking Change] Typescript 3.7 converts reference paths to tsconfig specified paths breaking multi-project compiles. Feb 13, 2020
@DanielRosenwasser
Copy link
Member

@weswigham wasn't there another similar issue you'd been involved in?

@sheetalkamat
Copy link
Member

Does https://github.com/microsoft/TypeScript/compare/experimentRelativePaths help this.. I had prototyped this for internal team who was running into similar issue but later they said they will work around it.

@Griffork
Copy link
Author

@sheetalkamat I don't know enough about tsc's code to know what that commit is doing.

@sheetalkamat
Copy link
Member

@Griffork You can build from that branch and see if that fixes your issue.

@Griffork
Copy link
Author

@sheetalkamat sorry I don't really have time to do that at the moment due to various work deadlines and home circumstances 😟. I just made a temporary fix so I could keep working (a post-build script that fixes all the paths) while waiting for replies.

I will still need this fixed eventually or I'll have to revert but I'd rather not because the ?. operator is the best thing since the ! operator.

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label Mar 10, 2020
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Mar 10, 2020
@huntjosh
Copy link

huntjosh commented Apr 5, 2020

Running into the same issue on another multi-project, only occurs when adding the baseUrl property. Will try the custom branch.

@Griffork
Copy link
Author

Griffork commented Jun 8, 2020

@huntjosh did the custom branch work for you?

@jacobbubu
Copy link

Running into the same issue on another multi-project, only occurs when adding the baseUrl property. Will try the custom branch.

I've got the same issue as yours.

@Griffork
Copy link
Author

Griffork commented Sep 6, 2020

I just updated to 4.0 and this issue is present again (but in reverse?). I now have a file in my server which has decided to export a particular type using an inline import rather than a top-level import. The inline import is being resolved to a relative path through import/exports even though the original import statement uses a path alias:

Server's Actor.ts

import { Actor as ActorBase } from "@Common/GameMechanics/Actors/Actor";
import { Actor as ClientActor } from "@Client/GameMechanics/Actors/Actor/Actor"; //used for something else.
export module Actor {
	export type Definition = ActorBase.Definition;
}

Server's World.ts

import { World as ClientWorld } from "@Client/GameMechanics/Environment/World";
import { Actor } from "../Actors/Actor";
import { Structure } from "@Common/GameMechanics/Environment/Structure";

export module World {

	//---------- Definitions.
	var EntityDefinitions = new Map<string, Actor.Definition | Structure.Definition>();

	export function GetDefinition(name: string) {
		return EntityDefinitions.get(name);
	}
}

Server's World.ts output:

import { Structure } from "@Common/GameMechanics/Environment/Structure";
export declare module World {
    function GetDefinition(name: string): Structure.Definition | import("../../../Common/GameMechanics/Actors/Actor").Actor.Definition | undefined;
}

Is there a way for me to force these imports to always be top-level and not get erased? It's particularly weird to me that the import is being inlined ignoring a middle file.

@Griffork
Copy link
Author

Griffork commented Sep 6, 2020

Since I upgraded to 4.0 I decided to check whether or not the initial issue was fixed, and it is not. I'm still getting @client paths inserted in my output files even though they're not present in any of the inputs.

@Griffork
Copy link
Author

@sheetalkamat Was that branch included in 4.0?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
Development

No branches or pull requests

6 participants