You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Offer option to resolve module path maps in emitted code #26722
Per #10866, module path maps are not resolved in emitted code. This means that if you attempt to run the emitted code with node, it will be unable to resolve those paths and the server will not start.
Current solutions:
use browserify or webpack to bundle the output -- this is an unfortunate solution given that it'd be the only reason for me to bring in one of these tools, as I've managed to build and deploy apps using just the typescript compiler. This blog post from the TypeScript team even strongly recommends using a TypeScript toolchain:
For that reason, we feel tsc and the tools around the compiler pipeline will still give the most integrated and consistent experience for most projects.
use a tool like module-alias. This is a bit gross as it requires duplicating the path maps, and inserting code at the root of your server.
Proposed solution:
Offer a compilerOption, something like resolvePaths, that resolves aliased paths to their relative paths.
Use Cases
So that I can continue to meaningfully use the paths property in tsconfig.json.
Just to elaborate a bit here, we haven't moved on that issue given that there are so many moving parts in the modules space that anything we do would be irrelevant or deprecated in 2 years.
I don't think this is a duplicate of #16577 -- this issue deals with aliases not being resolved to their relative paths on build. I should have been clearer in the description rather than linking to an old bug.
E.g, given this directory structure:
tsconfig.json
common
- helper.ts
server
- index.ts
Where tsconfig.json maps common to @common, like this:
When TSC outputs this, it keeps the alias rather than resolve it to the relative path. And node doesn't know how to resolve this.
Unfortunately, I don't think you can use tsconfig-paths when running node against JavaScript files. I use tsconfig-paths to run my server in development, e.g;
My request is that the TypeScript compiler offer an option to resolve the aliases to relative paths so that node can properly run the outputted code. Otherwise, the "paths" feature in tsconfig.json is somewhat unhelpful, as it requires bringing in additional tools to reconcile the output.
Reacted by Ian Copp, Alexander Trefz, Jakub Neander, Martin Kasban Tange, Marko Cen, Mike Mitterer, Matthew Little, Alberto Montañez, Bela Arany, Marcus Ahlfors and 12 more
Reacted by Bela Arany, Marcus Ahlfors, Anton Tryputen and Jacky Jiang
A simple use case that tsconfig-paths doesn't really help with:
I have package @companyName/some-shared-frontend-stuff that's written in Typescript
I want to use path aliases internally in @companyName/some-shared-frontend-stuff
I want to use that package in @companyName/site-number-1 that's written in JS with Webpack compiling
I don't want @companyName/site-number-1 to know or care about the internals of @companyName/some-shared-frontend-stuff and just use plain imports (e.g. import { WhateverComponent } from '...')
Right now, the only way to do this is to have a Babel compile step happen with @companyName/some-shared-frontend-stuff, which complicates dev work pretty painfully because I can't just use tsc -b --watch with npm link and instead I have to have a separate file watcher that re-does Babel builds when things change.
Until officially supported, I've created a solution that does not require any dependencies nor babel. It also fixes .d.ts files (currently only if they are in the same directory as output. declarationDir support can be added too. PRs are welcomed!). https://github.com/joonhocho/tscpaths
Reacted by Sophie Kirschner, Den, Peter Petrov and Matthew Little
Daniel Rosenwasser (@DanielRosenwasser) please reevaluate this issue. The proposed solution is simple, and has nothing to do with the reasons you mentioned for rejection - this doesn’t align with any 3rd party tool, it just makes the TS internal paths option work like the vast majority of people expect. Please consider this.
Every week an issue is opened about this, and quite a few have very long and very strong comment sections that all echo the same 2 things:
This is incredibly unintuitive which wastes a ton of time and turns users away from the language because they get a "successful" compile that yields code that can't be run
The only thing people want is a simple string replace at compile time that turns the paths path back into normal paths. That this is achievable is shown by tools like rollup - which is doing JUST THAT, without any confusion or problems - it just works. Like it should out of the box in tsc
Reacted by Ian Copp, Dmitry Ryumkin, Jonathan Windridge, Kresten, Em, John Crowe, Denis Olsem, Tim Basel, tarsupin, hezhuojie and 13 more
Andy (Andrewkraft) (@Andy-MS) could I get your eyes on this issue? I think Daniel errantly resolved this as a duplicate a while ago, but per my explanation above (#26722 (comment)), it's not.
I think given the reactions to this FR and the original issue (#10866), it would be a well received addition to TS.
Here are some questions I have about this feature:
What happens when an import resolves to a .d.ts file? Should these paths be rewritten too?
Should it be possible that importing a/b and a/c would write a/b but not a/c as a result?
Does this apply to all imports that start with a non-relative name, or just those where path mapping was invoked?
Does a path mapping that only changes the case of a path qualify under the above?
What if I only want to rewrite some of my paths?
Should this treat symlinks as opaque, or transparent?
Does this apply when an import resolves to an ambiently-declared module, but that import path also has an applicable path mapping?
If "yes", this means adding a .d.ts file to your compilation could break the runtime output of your program. This is unprecedented.
If "no", then how does a user predict whether a given import will be rewritten?
Should paths in emitted .d.ts files get rewritten? Why or why not?
What about reference directives?
What happens if an import resolves to a .ts file in node_modules? Should TS write out ../../node_modules/foo/index.ts ?
If not, by what logic is this excluded?
What happens under --allowJs when an import resolves to a .js file?
Again though, I still consider this a non-starter feature, because the statement
importafrom"b";
is an ECMAScript statement, and per design goal 7, when it doesn't need to be downleveled, we do not change it. This is no different from saying TS should cause a runtime error when a divide-by-zero occurs: We don't change the semantics of JS code.
Ryan Cavanaugh (@RyanCavanaugh) I think what people ask for could be a rather simple solution and has nothing to do with the existing compile-time module resolve process. I think what people want is:
Only apply this feature to the current compile result (i.e. after all existing compile logic is complete and just before emitting the result)
Works as the simple string replacement without involving any complicated path resolution processes.
e.g. for the config below:
The compliler should complete all existing compilation logic and do the followings before emitting the result:
check if any imported module names in the compile result meet the following criteria:
fully match or start with the string "a/b/c/" or fully match string "a/b/c"
If so, replace the string a/b/c in the module name with "../x/y/z" in the compile result that is about to emit (without validating or resolve the path ../x/y/z).
e.g. require("a/b/c/moduleA") becomes require("../x/y/z/moduleA")
require("a/b/c") becomes require("../x/y/z")
import xxx from "a/b/c/moduleA"; becomes import xxx from "../x/y/z/moduleA";
fully match or start with the string "@e/d/f/" => replace with "../../e/d/f"
e.g. require("@e/d/f/moduleB") becomes require("../../e/d/f/moduleB")
import xxx from "@e/d/f/moduleB"; becomes import xxx from "../../e/d/f/moduleB";
fully match or start with the string "../../u/v/w/" => replace with "../dist/"
e.g. require("../../u/v/w/moduleC") becomes require("../dist/moduleC")
import xxx from "../../u/v/w/moduleC"; becomes import xxx from "../dist/moduleC";
Moreover:
this module name rewritten logic should not only apply to .js but also .d.ts files that are about to emit.
this feature should work independently. i.e. whether or not existing path mapping feature or project references feature is on or off doesn't impact the on/off of this feature as this feature works as the last step of the compilation process just before emitting.
this feature should apply to any /// <reference types="xxx/xxx/xx" /> as well
I think the simple logic above should avoid most of the issues you mentioned in your post as this module name rewritten logic is triggered by string match and processed by string replacement without any module resolution process involved.
Curious if anyone has found a solution for rewriting import alias in *.d.ts files? We're using babel to build TS to JS which resolves the alias correctly, but it's broken in *.d.ts.
Viktor Vincze (@wintercounter) Hm. Doesn't seem to work if your tsconfig.jsonextends from another config. In my project, we extend from a dependency config and tscpaths blows up.
On Wed, Jan 8, 2020 at 5:00 PM Tom ***@***.***> wrote:
@wintercounter <https://github.com/wintercounter> Hm. Doesn't seem to
work if your tsconfig.json extends from another config. In my project, we
extend from a dependency config and tscpaths blows up.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#26722?email_source=notifications&email_token=AAHLJQBLU7OCXGYZOC6C2ITQ4X2BDA5CNFSM4FSCEI7KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEINBRAA#issuecomment-572135552>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAHLJQHWFLAQHJPBTQQK4RLQ4X2BDANCNFSM4FSCEI7A>
.
After reading a heavy portion of this thread I can say that everyone is right, from their perspective.
What I (again. I) think the problem in this discussion is?
TS users come from a working mode of, hey, i'm running my node app with TS in development (ts-node/webpack/whatever) and it works!!
Now, when I build it does not!
While (I guess) TS authors look at it differently, TS is just a platform to take on form of source code and output another form of source code and having it run as intended.
This came to bloom mainly because of the gaining popularity of monorepos and how easy it is to build libraries that way, alongside a working application.
Now, there are 2 scenarios:
The app and libraries are both compiled to a DIST folder and run from it
The app is compiled to a DIST folder, the libraries are compiled but published to a package repository (e.g NPM)
With (1), users expect TS to take those paths mappings and convert them to relative paths so they will work cause they are not in node_modules....
With (2) user's will not expect that to happen because it will cause the imports to point to nothing.
I understand TS's team take on this, it is something that will work with (1) but not (2) and that's enough to rule it out.
Maybe i'm wrong, but it seems to me it's one of the reasons for the mixup
After reading a heavy portion of this thread I can say that everyone is right, from their perspective.
What I (again. I) think the problem in this discussion is?
TS users come from a working mode of, hey, i'm running my node app with TS in development (ts-node/webpack/whatever) and it works!!
Now, when I build it does not!
While (I guess) TS authors look at it differently, TS is just a platform to take on form of source code and output another form of source code and having it run as intended.
This came to bloom mainly because of the gaining popularity of monorepos and how easy it is to build libraries that way, alongside a working application.
Now, there are 2 scenarios:
1. The app and libraries are both compiled to a DIST folder and run from it
2. The app is compiled to a DIST folder, the libraries are compiled but published to a package repository (e.g NPM)
With (1), users expect TS to take those paths mappings and convert them to relative paths so they will work cause they are not in node_modules....
With (2) user's will not expect that to happen because it will cause the imports to point to nothing.
I understand TS's team take on this, it is something that will work with (1) but not (2) and that's enough to rule it out.
Maybe i'm wrong, but it seems to me it's one of the reasons for the mixup
Optional flag = Developer will have the option to enable it for #1 of your case; which is the majority of the cases as can be seen by the number of upvotes here.
We took this to several design meetings and discussed this to death, and we're sticking with the "JS is JS" design philosophy that has driven TypeScript since its origination.
I'm going to lock this because it's a long thread and our typical experience is that these threads go in circles when it takes more than a few minutes to read the thread. As usual, I'd implore anyone to actually read all 81 comments here, as we ourselves have done multiple times, before filing new issues related to the matter.
Activity
DanielRosenwasser commentedon Aug 28, 2018
I think you want #16577.
DanielRosenwasser commentedon Aug 28, 2018
Just to elaborate a bit here, we haven't moved on that issue given that there are so many moving parts in the modules space that anything we do would be irrelevant or deprecated in 2 years.
For ts-node, can you use tsconfig-paths, as per this documentation?
dannycochran commentedon Aug 29, 2018
Hey Daniel, thanks for the quick response.
I don't think this is a duplicate of #16577 -- this issue deals with aliases not being resolved to their relative paths on build. I should have been clearer in the description rather than linking to an old bug.
E.g, given this directory structure:
Where
tsconfig.jsonmapscommonto@common, like this:And this code in
server/index.ts:When TSC outputs this, it keeps the alias rather than resolve it to the relative path. And node doesn't know how to resolve this.
Unfortunately, I don't think you can use
tsconfig-pathswhen runningnodeagainst JavaScript files. I usetsconfig-pathsto run my server in development, e.g;node -r ts-node/register -r tsconfig-paths server/index.tsBut this does not work:
node -r tsconfig-paths server-build/index.jsMy request is that the TypeScript compiler offer an option to resolve the aliases to relative paths so that node can properly run the outputted code. Otherwise, the "paths" feature in
tsconfig.jsonis somewhat unhelpful, as it requires bringing in additional tools to reconcile the output.deejayy commentedon Sep 13, 2018
Check out this gist:
https://gist.github.com/deejayy/aa5f0cde76dc29f6b4127e60e74be2f2
It resolves the paths from tsconfig.json. Dirty hack, but works.
icopp commentedon Sep 13, 2018
A simple use case that
tsconfig-pathsdoesn't really help with:@companyName/some-shared-frontend-stuffthat's written in Typescript@companyName/some-shared-frontend-stuff@companyName/site-number-1that's written in JS with Webpack compiling@companyName/site-number-1to know or care about the internals of@companyName/some-shared-frontend-stuffand just use plain imports (e.g.import { WhateverComponent } from '...')Right now, the only way to do this is to have a Babel compile step happen with
@companyName/some-shared-frontend-stuff, which complicates dev work pretty painfully because I can't just usetsc -b --watchwithnpm linkand instead I have to have a separate file watcher that re-does Babel builds when things change.dannycochran commentedon Sep 13, 2018
Daniel Rosenwasser (@DanielRosenwasser) provided my previous comment makes sense, can we remove the duplicate flag or discuss further?
dannycochran commentedon Sep 28, 2018
Daniel Rosenwasser (@DanielRosenwasser) pinging again. Any chance this could go on the roadmap for 3.2?
joonhocho commentedon Oct 8, 2018
Until officially supported, I've created a solution that does not require any dependencies nor babel. It also fixes .d.ts files (currently only if they are in the same directory as output. declarationDir support can be added too. PRs are welcomed!).
https://github.com/joonhocho/tscpaths
dannycochran commentedon Nov 8, 2018
Joon Ho Cho (@joonhocho) there are existing third party solutions, the idea is to avoid them entirely.
Daniel Rosenwasser (@DanielRosenwasser) pinging again -- this is still marked as a duplicate and it shouldn't be. can we re-evaluate?
alexandertrefz commentedon Nov 30, 2018
Daniel Rosenwasser (@DanielRosenwasser) please reevaluate this issue. The proposed solution is simple, and has nothing to do with the reasons you mentioned for rejection - this doesn’t align with any 3rd party tool, it just makes the TS internal paths option work like the vast majority of people expect. Please consider this.
Every week an issue is opened about this, and quite a few have very long and very strong comment sections that all echo the same 2 things:
This is incredibly unintuitive which wastes a ton of time and turns users away from the language because they get a "successful" compile that yields code that can't be run
The only thing people want is a simple string replace at compile time that turns the paths path back into normal paths. That this is achievable is shown by tools like
rollup- which is doing JUST THAT, without any confusion or problems - it just works. Like it should out of the box intscdannycochran commentedon Dec 1, 2018
Andy (Andrewkraft) (@Andy-MS) could I get your eyes on this issue? I think Daniel errantly resolved this as a duplicate a while ago, but per my explanation above (#26722 (comment)), it's not.
I think given the reactions to this FR and the original issue (#10866), it would be a well received addition to TS.
dannycochran commentedon Dec 4, 2018
Andy (Andrewkraft) (@Andy-MS) ah my mistake I read through the issue more and understand how this is a dupe. Thanks for taking a look.
62 remaining items
t83714 commentedon Nov 30, 2019
Ryan Cavanaugh (@RyanCavanaugh) I think what people ask for could be a rather simple solution and has nothing to do with the existing compile-time module resolve process. I think what people want is:
e.g. for the config below:
{ "compilerOptions": { "pathAlias": { "a/b/c" : "../x/y/z", "@e/d/f/": "../../e/d/f", "../../u/v/w/": "../dist/" } } }The compliler should complete all existing compilation logic and do the followings before emitting the result:
a/b/cin the module name with "../x/y/z" in the compile result that is about to emit (without validating or resolve the path../x/y/z).require("a/b/c/moduleA")becomesrequire("../x/y/z/moduleA")require("a/b/c")becomesrequire("../x/y/z")import xxx from "a/b/c/moduleA";becomesimport xxx from "../x/y/z/moduleA";require("@e/d/f/moduleB")becomesrequire("../../e/d/f/moduleB")import xxx from "@e/d/f/moduleB";becomesimport xxx from "../../e/d/f/moduleB";require("../../u/v/w/moduleC")becomesrequire("../dist/moduleC")import xxx from "../../u/v/w/moduleC";becomesimport xxx from "../dist/moduleC";Moreover:
.jsbut also.d.tsfiles that are about to emit.on/offof this feature as this feature works as the last step of the compilation process just before emitting./// <reference types="xxx/xxx/xx" />as wellI think the simple logic above should avoid most of the issues you mentioned in your post as this
module name rewritten logicis triggered by string match and processed by string replacement without any module resolution process involved.t83714 commentedon Nov 30, 2019
It also achieves what we ask for using
babel&babel-plugin-module-resolver(just in case anyone looks for alternative solution):@babel/cli&babel-plugin-module-resolveryarn add --dev @babel/cli babel-plugin-module-resolver.babelrcfile at the project root with the following content:{ "compact": false, "retainLines": true, "minified": false, "inputSourceMap": false, "sourceMaps": false, "plugins": [ [ "module-resolver", { "root": ["./dist"], "alias": { "abc/src": "@a/bc/dist" } } ] ] }yarn tsc -b && babel dist -d distdistdirectory.tscdistdirectory throughbabeland replace the module name and output back to the samedistdirectoryUnfortunately, babel won't touch
*.d.tsfiles in thedistdirectory.It will be good that this feature can be implemented by
tscor make it possible to create a plugin oftscto do so.tconroy commentedon Jan 7, 2020
Curious if anyone has found a solution for rewriting import alias in
*.d.tsfiles? We're using babel to build TS to JS which resolves the alias correctly, but it's broken in *.d.ts.wintercounter commentedon Jan 7, 2020
Yes, https://www.npmjs.com/package/tscpaths
UPDATE: Sorry, linked wrong package first.
tconroy commentedon Jan 8, 2020
Viktor Vincze (@wintercounter) Hm. Doesn't seem to work if your
tsconfig.jsonextendsfrom another config. In my project, we extend from a dependency config andtscpathsblows up.wintercounter commentedon Jan 8, 2020
shlomiassaf commentedon Jan 16, 2020
After reading a heavy portion of this thread I can say that everyone is right, from their perspective.
What I (again. I) think the problem in this discussion is?
TS users come from a working mode of, hey, i'm running my node app with TS in development (ts-node/webpack/whatever) and it works!!
Now, when I build it does not!
While (I guess) TS authors look at it differently, TS is just a platform to take on form of source code and output another form of source code and having it run as intended.
This came to bloom mainly because of the gaining popularity of monorepos and how easy it is to build libraries that way, alongside a working application.
Now, there are 2 scenarios:
With (1), users expect TS to take those paths mappings and convert them to relative paths so they will work cause they are not in
node_modules....With (2) user's will not expect that to happen because it will cause the imports to point to nothing.
I understand TS's team take on this, it is something that will work with (1) but not (2) and that's enough to rule it out.
Maybe i'm wrong, but it seems to me it's one of the reasons for the mixup
eyedean commentedon Jan 21, 2020
Optional flag = Developer will have the option to enable it for #1 of your case; which is the majority of the cases as can be seen by the number of upvotes here.
winseros commentedon Jan 31, 2020
If path maps are not resolved into emitted code, then what are they for?
DanielRosenwasser commentedon Jan 31, 2020
Nikita Kobzev (@winseros) this heavily downvoted comment I wrote a few months ago answers that question. #26722 (comment)
RyanCavanaugh commentedon Feb 1, 2020
We took this to several design meetings and discussed this to death, and we're sticking with the "JS is JS" design philosophy that has driven TypeScript since its origination.
I'm going to lock this because it's a long thread and our typical experience is that these threads go in circles when it takes more than a few minutes to read the thread. As usual, I'd implore anyone to actually read all 81 comments here, as we ourselves have done multiple times, before filing new issues related to the matter.