Typescript: Offer option to resolve module path maps in emitted code

Created on 29 Aug 2018  Â·  82Comments  Â·  Source: microsoft/TypeScript

Search Terms

paths
tsconfig.json
compilerOptions
ts-node
node
cannot find module

Suggestion

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:

  1. 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:

https://blogs.msdn.microsoft.com/typescript/2018/08/27/typescript-and-babel-7/

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.

  1. 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.

Examples

Please see the example in #10866.

Checklist

My suggestion meets these guidelines:

  • [x] This wouldn't be a breaking change in existing TypeScript / JavaScript code
  • [ ] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [x] This isn't a runtime feature (e.g. new expression-level syntax)
Declined Suggestion

Most helpful comment

It's so different from 16577, and more important!

While this issue and https://github.com/microsoft/TypeScript/issues/16577 look similar in terms of "_customizing emitted code on module paths_", I think there is a subtle difference between the two and I'd like to ask for reopening this issue as its own.

Let's say I have the following import in my typescript code

import helper from "@utils/helper";`

and in tsconfig.json I have set:

"paths":  {
   "@utils/*": [ "src/interal/tools/utils/*" ],
}

The problem with this one is, there is an actual piece of information, (rather than just a .js extension like https://github.com/microsoft/TypeScript/issues/16577), that is getting lost. It's because tsc just emits the same path as following:

const helper_1 = __importDefault(require("@utils/helper"))

and the piece of information that @util is actually pointing out to somewhere specific in my codebase (i.e. src/internal/tools/utils) is swallowed by the compiler! Unlike the .js extension issue, there is no way on earth that one can infere this mapping and fix this code, only by looking at the output of the compiler.


Exesting Third Party Solutions

To solve this issue, I need to either re-enter the same mapping manually for the consumption of my JS Loader via something like module-alias (funny, I'm already doing it once for ESLint!) which requires having multiple entries in the codebase for the same concept, a terrible practice; or I should use a tool like tsconfig-paths that reads tsconfig.json and does what compiler is failing to do!

It's interesting to me that for this very problem, tsconfig-paths has got to 500K downloads/week! In the mean time, all the tickets asking for this simple functionality have been closed here: https://github.com/Microsoft/TypeScript/issues/16640, https://github.com/microsoft/TypeScript/issues/18951, https://github.com/microsoft/TypeScript/issues/18972, https://github.com/microsoft/TypeScript/issues/19453, and so on! (PS. Just found https://github.com/Microsoft/TypeScript/issues/10866 from years ago with tons of emojis!)


Proposal

It's really simple.

tsconfig.json already has 89 flags. Let's add the 90th one as --resolveMappedPaths (or something similar) which, at the time of compile, will replace any mapped path (coming from the "key" side of the property in the paths object) to its destination ("value" side of the same property!)

So my tsconfig.json would be:

   "compilerOptions": {
      "module": "commonjs",
      "esModuleInterop": true,
      "target": "es6",
      "noImplicitAny": true,
      "moduleResolution": "node",
      resolveMappedPaths: "true"
      "..."

It can also be a property on an overloaded per-path-basis config, e.g.:

"paths":  {
   "@utils/*": { 
       "resolveOnCompile": true,
       "target": [ "src/interal/tools/utils/*" ]
   },
}

All 82 comments

I think you want #16577.

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?

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:

tsconfig.json
common
  - helper.ts
server
  - index.ts

Where tsconfig.json maps common to @common, like this:

compilerOptions: {
  ...
  "paths": {
    "@common/*": [
      "./common/*"
    ]
  }
}

And this code in server/index.ts:

# server/index.ts
import { foo } from '@common/helper';

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;

node -r ts-node/register -r tsconfig-paths server/index.ts

But this does not work:

node -r tsconfig-paths server-build/index.js

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.

Check out this gist:
https://gist.github.com/deejayy/aa5f0cde76dc29f6b4127e60e74be2f2
It resolves the paths from tsconfig.json. Dirty hack, but works.

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.

@DanielRosenwasser provided my previous comment makes sense, can we remove the duplicate flag or discuss further?

@DanielRosenwasser pinging again. Any chance this could go on the roadmap for 3.2?

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

@joonhocho there are existing third party solutions, the idea is to avoid them entirely.

@DanielRosenwasser pinging again -- this is still marked as a duplicate and it shouldn't be. can we re-evaluate?

@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:

1) 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_

2) 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

@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 (https://github.com/Microsoft/TypeScript/issues/26722#issuecomment-417020795), 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.

I think it's unlikely that we would change the module specifier you wrote when emitting -- #16577 is an existing issue that asks for that kind of functionality, and if we ever did add that then maybe we would take a look at this too.

@andy-ms ah my mistake I read through the issue more and understand how this is a dupe. Thanks for taking a look.

It's so different from 16577, and more important!

While this issue and https://github.com/microsoft/TypeScript/issues/16577 look similar in terms of "_customizing emitted code on module paths_", I think there is a subtle difference between the two and I'd like to ask for reopening this issue as its own.

Let's say I have the following import in my typescript code

import helper from "@utils/helper";`

and in tsconfig.json I have set:

"paths":  {
   "@utils/*": [ "src/interal/tools/utils/*" ],
}

The problem with this one is, there is an actual piece of information, (rather than just a .js extension like https://github.com/microsoft/TypeScript/issues/16577), that is getting lost. It's because tsc just emits the same path as following:

const helper_1 = __importDefault(require("@utils/helper"))

and the piece of information that @util is actually pointing out to somewhere specific in my codebase (i.e. src/internal/tools/utils) is swallowed by the compiler! Unlike the .js extension issue, there is no way on earth that one can infere this mapping and fix this code, only by looking at the output of the compiler.


Exesting Third Party Solutions

To solve this issue, I need to either re-enter the same mapping manually for the consumption of my JS Loader via something like module-alias (funny, I'm already doing it once for ESLint!) which requires having multiple entries in the codebase for the same concept, a terrible practice; or I should use a tool like tsconfig-paths that reads tsconfig.json and does what compiler is failing to do!

It's interesting to me that for this very problem, tsconfig-paths has got to 500K downloads/week! In the mean time, all the tickets asking for this simple functionality have been closed here: https://github.com/Microsoft/TypeScript/issues/16640, https://github.com/microsoft/TypeScript/issues/18951, https://github.com/microsoft/TypeScript/issues/18972, https://github.com/microsoft/TypeScript/issues/19453, and so on! (PS. Just found https://github.com/Microsoft/TypeScript/issues/10866 from years ago with tons of emojis!)


Proposal

It's really simple.

tsconfig.json already has 89 flags. Let's add the 90th one as --resolveMappedPaths (or something similar) which, at the time of compile, will replace any mapped path (coming from the "key" side of the property in the paths object) to its destination ("value" side of the same property!)

So my tsconfig.json would be:

   "compilerOptions": {
      "module": "commonjs",
      "esModuleInterop": true,
      "target": "es6",
      "noImplicitAny": true,
      "moduleResolution": "node",
      resolveMappedPaths: "true"
      "..."

It can also be a property on an overloaded per-path-basis config, e.g.:

"paths":  {
   "@utils/*": { 
       "resolveOnCompile": true,
       "target": [ "src/interal/tools/utils/*" ]
   },
}

+1

+10086

For production apps after a tsc build.
I have created a TS Paths replacer in Go which is pretty fast and that addresses the custom paths issue.
After it's applied you only need to run your app node main.js.

@joseluisq Thanks! But @DanielRosenwasser I don't get it why we don't get a compiler flag to resolve the path. Just to make the path-feature work we have to usw webpack or another bundler. It does not work with tsc.
You say "it's our main goal not to change the generated JS code" but path-maps with tsc breaks the code!!!!
I have two options - Use webpack for this problem to solve or stay with those ugly, long, import path statements...

You say "it's our main goal not to change the generated JS code" but path-maps with tsc breaks the code!!!!

Path mappings are meant to reflect the behavior of whatever is actually resolving .js files. The idea is not to break your code, but to reflect the behavior of something like webpack's resolve.alias or AMD's paths field.

I don't get it why we don't get a compiler flag

Because a compiler flag doesn't actually relieve us of the burden of the complexity of a feature - it just makes it harder for us to think about.

@DanielRosenwasser Thanks for your answer!

the burden of the complexity of a feature
For this particular case??? Seems not sooo complex to me.

My main problem is that I really like what tsc gives me - multiple output files! Every TS-file produces a JS counterpart. That's cool. Readable, understandable and nice. That's what I use for all of my libs. I use webpack for the main application - OK.

is not to break your code
But this is what tsc produces without the help of WebPack - shouldn't tsc produce valid AND runnable code just out of the box???

I can choose between two options.

  • TS-Path-Mappings / WebPack for my libs and loosing the multi-file-output-mode (you know what I mean)
  • No TS-Path-Mappings but accept this ugly 'import ../../../whatever' style
    Both options are just a pain in the ass... (AND it destroys the great TS user experience)

[Update]
Hmmm. I found this plugin for ttypescript - it seems to work but I still think this should be part of the official tsc-compiler.
If you don't want to support the mentioned compiler flag, another way to open TS/tsc would be a better support for transformers e.g. like ttypescript . I understand that you want to be as close as possible to JS but on the other hand at least a bit more flexibility would really help

but to reflect the behavior of something like webpack's resolve.alias

But when you use that in webpack, the resulting build actually works. When you use path mapping in TS and use the TS compiler, the resulting build doesn't work.

+1 for this, same issue here. We're using TSC only for type-check and generating d.ts files. The rest is Babel. In Babel we're resolving our aliases fine, but the generated d.ts files will still have the alieses in the copiled code. I also don't understand why this issue have to be closed.

+1

+1.....

@wintercounter >I also don't understand why this issue have to be closed.
Nobody does! Is this really OpenSource????
Microsoft blocks this feature in a very dictatorial manner

Also wondering - this is really making a theoretically nice feature (having nice imports) completely useless.
I mean I don't have any Idea how hard this is to implement but from a naive though it's just s/@alias/realPathFromTSConfig ... no?

Some people need to learn to read.

The issue is closed because it is a duplicate. The original author indicated that they understood it as such. When the original author agrees it is a duplicate, it is a duplicate, no matter what you think and #16577 is still open people!

I agree with your first statement (so please go through the comments). Most of the people doesn't consider this as a duplicate and neither do I. The mentioned issue is a whole different topic and that's the main reason this thread could grow as huge as now.

If it is a different issue than the OP, who agreed it was a duplicate then open a new issue that isn't a duplicate that better describes the problem/proposal because it is a different issue...

I went through all the comments of that issue and find no relevance to our
problem.

On Thu, Aug 22, 2019, 00:16 Kitson Kelly notifications@github.com wrote:

If it is a different issue than the OP, who agreed it was a duplicate then
open a new issue that isn't a duplicate that better describes the
problem/proposal because it is a different issue...

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/microsoft/TypeScript/issues/26722?email_source=notifications&email_token=AAHLJQAEHCFEIKQO3LPESCTQFW5DVA5CNFSM4FSCEI7KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD43JE5Q#issuecomment-523670134,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAHLJQEAQ6AP75R544A4HBTQFW5DVANCNFSM4FSCEI7A
.

@kitsonk Are you sure you red correctly?
@dannycochran nailed it ONE!!!! year ago: https://github.com/microsoft/TypeScript/issues/26722#issuecomment-417020795
What should we do to get this feature - start a petition?? It can't be so hard to solve this problem.

In my opinion it's a philosophical, MS internal, debate that holds back this feature (BTW we shouldn't call it feature - it solves ab bug. Using only tsc to generate code, breaks the app/lib if you use path mappings)

However, (imagine me on my knees), PLEASE add this feature (solve this bug)!

I can understand the team concerns if they have technical reasons to don't implement the feature.

But if TS provides path aliases at development time why those are not also supported in building process via tsc? Why I need a third-party dependency for a TS feature?

It's been two months since I posted a proposal above.

The situation we are in is like going to McDonald's and asking for a burger without a bun. Then, in a big surprise, you hear "_we are not going to cook that thing_" and "_oh, it's similar to the request of other customers who wanted a veggie burger with yellow bell pepper and avocado._" Unfortunately, none of them are accurate, and I can unblock myself by buying a burger and tossing away the buns manually. But, due to the extra overhead and the shocking way I am treated, I'll probably reconsider visiting there again in the future.

Same is here. People in the Open Source Community totally understand if a request has less priority than other works. However, keeping a clear request with an accurate proposal "closed as duplicate" and not caring at all can hurt. It hurts the emotions of the current customers of the platform (developers) at first, but then such demoralization will eventually lead them to shop elsewhere in future. That's when it might ultimately hurt back the community/project/team.

At the end of the day, what makes a project alive is just how users of the project feel they are treated. There are tons and tons of dead projects and dead packages that people stopped using because no one hears them (just hear them and acknowledge them) when they have a legit request. And it's totally rational -- as an architect or senior software engineer in charge of a big project you don't want to invest 6 months on something in future and then when you face a problem that hunderds of others are facing too, you see the community has closed the door with "_WE DON'T CARE_" sign, instead of "_Oh, we hear you. That's right. Let's democrazie it and post a 'Help Wanted' sign on this to see who will volunteer. :)_"


To recap:

Is what we want a duplicate of the issue that marked as duplicate?

Absolutely NOT.

They can fall into the same category of "custom module specifier", just the way you can put chicken and burger in the category of "food". :)

In fact, it was @andy-ms who mentioned that internally they are thinking about a dependency of implementation:

I think it's unlikely that we would change the module specifier you wrote when emitting -- #16577 is an existing issue that asks for that kind of functionality, and if we ever did add that then maybe we would take a look at this too.

It's like saying when we start serving chicken with lettuce then we will serve burger with no bun. Sure, it's your call internally how you want to build dependencies. But it doesn't mean I should now sit and wait for chicken with lettuce!

Is this a breaking feature?

Absolutely NOT.

We are asking for a new flag next to the existing 89 ones.

Is this a random irrelevant just-fancy feature?

Absolutely NOT.

There is no way that you can natively use path mapping in TypeScript without using a third-party for this problem. And without path-mapping if you ever want to move around your src/components/forms directory, you need to change 100 files. Or if you want to include a component in another one you'll need to walk barefoot using ../../!

Is this a new problem to solve from the scratch?

Absolutely NOT.

There are already a lot of third-party solutions just for this problem!

Is this going to be so new and hard to do in TSC world?

Absolutely NOT.

TSC provides path-aliases at the development time, it's just the way the output should be created.


Again, I have unblocked myself using a custom burger from Burger King! And it's going to cost me 5 minutes to copy/paste and set up the same custom third-party solutions in any new project. (Already done twice!)

I'm just watching here to see how Microsoft is treating its Typescript users and that will definitely shape my thoughts about Typescript for any future technical decision I'll make. :)

Thanks!

slowly backs away from keyboard

How is this thread so long without a single example of what the proposed transform is?

I'll also paste what I wrote elsewhere: Our general take on this is that you should write the import path that works at runtime, and set your TS flags to satisfy the compiler's module resolution step, rather than writing the import that works out-of-the-box for TS and then trying to have some other step "fix" the paths to what works at runtime.

We have about a billion flags that affect module resolutions already (baseUrl, path mapping, rootDir, outDir, etc). Trying to rewrite import paths "correctly" under all these schemes would be an endless nightmare.

IOW if there's some way where TS could allow you to write the path that you want emitted, that will always be the preferred solution. In general these are almost always possible with path mappings; if you have a scenario that can't be addressed with path mappings, that's the suggestion we're much more likely to be open to.

Hi folks -- OP here. Maybe chill a little. The vitriol / victimhood / name calling is really bizarre for a product that consistently releases new features and incorporates community feedback. FWIW, my solution to this was to stop using tsconfig paths and live with the ../../.. here and there. It's been fine. My codebase has not imploded.

How is this thread so long without a single example of what the proposed transform is?

@RyanCavanaugh This comment kind of illustrates what path we'd want emitted.

This alias:

# server/index.ts
import { foo } from '@common/helper';

Should get resolved to:

# server-build/server/index.js
const helper_1 = require('../common/helper');
const foo = helper_1.foo;

That presumes an outDir of server-build, which, when built, would look like this:

  common
    - helper.js
  server
    - index.js

tsconfig.json:

compilerOptions: {
  ...
  "paths": {
    "@common/*": [
      "./common/*"
    ]
  }
}

This is also closely related to #30952, which is basically the same thing as @dannycochran and @eyedean have noted above but for .d.ts files.

@dannycochran what rules should tsc follow to determine to rewrite?

# server/index.ts
import { foo } from '@common/helper';

Is the @ suggesting some sort of semantic meaning? That directly conflicts with Node.js resolution logic with namespaced packages. What would the tsconfig.json look like?

Also, if based on your first comment you expect paths to contained overloaded functionality, that would be a breaking change for a lot of other users of TypeScript who are trying to model what their runtime environment is looking like, the original intention of the poorly named paths argument (which was to model configurable loaders like AMD and SystemJS, and therefore copied the misleading name of such a configuration option there).

So you would need to propose introducing some sort of other variable that said "ignore the existing meaning of paths and resolve and rewrite the paths relative to something else", but also the feature would need to address where it isn't possible to express a relative path at runtime, and those who would need to mix runtime mapping and build time rewriting.

Also, if based on your first comment you expect paths to contained overloaded functionality

The functionality described in @dannycochran's comment is how paths already works. Something like this...

baseUrl: ".",
paths: {
  "~/*": ["src/*"]
}

...is already used by lots of people (including me) to make Typescript reflect the common uses or even default behavior of other Javascript tooling. (For example, the built-in ~ path alias in Parcel is effectively identical to the above for most use cases.)

Is the @ suggesting some sort of semantic meaning?

This is presumably...

baseUrl: ".",
paths: {
  "@common/*": ["src/*"]
}

The functionality described in @dannycochran's comment is how paths already works.

But it doesn't cause the import specifier to be rewritten. Taking away just the ability to remap locations of modules that will be located different during runtime, which is what the intent of paths is today needs to co-exist. Just putting a flag that says "rewrite" module specifiers wouldn't be a good idea, because as soon as you do that, someone will say they want remapping and rewriting at the same time.

This is presumably...

I think if this has any chance, presuming wouldn't be sufficient.

So a simple question: How we should ship libraries while using paths? Short answer: We can't by default.

To be fair, Babel doesn't support such either, I have to use a separate plugin to resolve my alieses. BUT at least it is able to have a plugin!

How would you ship a JavaScript library whose import paths don't work in the context of another program? It's the same question and has similarly few answers.

That's exactly the point. Currently it doesn't work. It only can work if those paths are being resolved.

Yeah, that is what I am struggling with here too...

Potential use cases for a library:

  1. One that will run under node. If this case, best to leverage node module resolution fully instead of trying to invent your own. node doesn't have a configurable loader. That is life.
  2. Running in a browser or node as a standalone library. If this is the case, you should use some sort of bundler, like rollup, webpack, Parcel, etc. Using paths might help make bundling easier, but unlikely, best to leverage your packager to output the standalone versions you need. Being a complete build tool is not a goal of TypeScript.
  3. Running browser only, using an AMD or SystemJS loader. You have one design time paths of which you have to inform your consumer on how to integrate that into a larger loader configuration at run time, but likely can releverage your paths.
  4. Loading ES Modules directly in a browser (or Deno). Use import-map (or the polyfill) and again, essentially the paths.

tsc is not a bundler, never will be a bundler (or a full featured build tool)... that means it will never know enough, nor want to know enough to properly re-write the module specifiers. It understands Node.js resolution, but again, that is design time mirroring of what is expected to be valid at runtime. For tsc rewriting can only be write for a very narrow set of uses cases and most of the time it would be just wrong.

Just repeatedly saying "its a bug, fix it" without addressing the specific questions raised to any proposal in really not going to make anything move forward. Proposing something that a) preserves existing functionality that people are depending on and b) can be usable with exemplar use cases is about the only way this will move forward.

For my specific case the biggest issue here are d.ts files. No bundler is
going to help to "bundle"/resolve their imports.

I understand the concerns behind implementing such feature but seems like
it's a valid use case for many of us. If tsc's plugin system would provide
us an interface where we can manipulate the emitted path that would solve
the problem for many of us I think. tsc can stay pure that way.

On Tue, Aug 27, 2019, 06:30 Kitson Kelly notifications@github.com wrote:

Yeah, that is what I am struggling with here too...

Potential use cases for a library:

  1. One that will run under node. If this case, best to leverage node
    module resolution fully instead of trying to invent your own. node doesn't
    have a configurable loader. That is life.
  2. Running in a browser or node as a standalone library. If this is
    the case, you should use some sort of bundler, like rollup, webpack,
    Parcel, etc. Using paths might help make bundling easier, but
    unlikely, best to leverage your packager to output the standalone versions
    you need. Being a complete build tool is not a goal of TypeScript.
  3. Running browser only, using an AMD or SystemJS loader. You have one
    design time paths of which you have to inform your consumer on how to
    integrate that into a larger loader configuration at run time, but likely
    can releverage your paths.
  4. Loading ES Modules directly in a browser (or Deno). Use import-map
    (or the polyfill) and again, essentially the paths.

tsc is not a bundler, never will be a bundler (or a full featured build
tool)... that means it will never know enough, nor want to know enough to
properly re-write the module specifiers. It understands Node.js resolution,
but again, that is design time mirroring of what is expected to be valid at
runtime. For tsc rewriting can only be write for a very narrow set of
uses cases and most of the time it would be just wrong.

Just repeatedly saying "its a bug, fix it" without addressing the specific
questions raised to any proposal in really not going to make anything move
forward. Proposing something that a) preserves existing functionality that
people are depending on and b) can be usable with exemplar use cases is
about the only way this will move forward.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/microsoft/TypeScript/issues/26722?email_source=notifications&email_token=AAHLJQHBP5BWREGJBL2Q2W3QGSUVVA5CNFSM4FSCEI7KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5GOA6Y#issuecomment-525131899,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAHLJQHD64R4EUVR5RZAWF3QGSUVVANCNFSM4FSCEI7A
.

A new Node.js proposal, proposal-pkg-exports, adds a new import field to package.json that seems to cover the most common use case there. If it would be accepted and TypeScript would use it for type resolution (I guess it can be tracked as a part of #33079) it would be possible to create local aliases in a simpler and more compatible way (webpack might support it as well), while paths option would be able to keep it's current meaning - reflecting non-standard runtime resolution behavior.

I am sorry to see that this issue is not receiving the attention it needs after over a year. This is a real problem and I don't understand why @eyedean 's proposal has fallen on deaf ears.

We're listening and have repeatedly, repeatedly, repeatedly explained in multiple threads why this proposal and similar ones go against our main goals about how module paths should work in TypeScript.

Not getting what you want doesn't mean the other person in the conversation isn't listening.

Thanks for you replay @RyanCavanaugh. Would you mind pointing me to one of the places where I can read up on the ideas behind module paths?

There's also primary design goal number 7: Preserve runtime behavior of all JavaScript code
https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

An import statement is JavaScript code, so unless it needs downleveling, per our design goals, it should be left alone.

Frankly, design goal 7 seems to indicate the exact opposite reaction. You have set up compiler config, you are changing your code for that compiler config, now the compiler is breaking your JavaScript and thus changing your runtime behaviour. Thats exactly why everybody wants these paths rewritten. Such a simple change, with so much fuss, is really a shame.

I appreciate all the efforts of the Typescript team, but the given reasons for preventing a change that is comparatively speaking so trivial and has, again relatively speaking, so little possible negative impact while such a big part of the userbase is asking for it is just baffling to me.

You have set up compiler config, you are changing your code for that compiler config, now the compiler is breaking your JavaScript and thus changing your runtime behaviour.

The only situations that it breaks behaviour is when you never had a valid behaviour to begin with. It just isn't magically fixing it for you, and that upsets you.

paths was introduced to mimic the potential runtime behaviour of different loaders, so you could write your TypeScript in a way that would match the runtime configuration.

I appreciate all the efforts of the Typescript team, but the given reasons for preventing a change that is comparatively speaking so trivial and has, again relatively speaking, so little possible negative impact while such a big part of the userbase is asking for it is just baffling to me.

Then make a proposal that would work. A proposal would include what gets emitted and the logic. People so far have only proposed that some sort of magical flag be added to the configuration that will read the developers mind and emit something, but haven't expressed some sort of valid emit.

Frankly, design goal 7 seems to indicate the exact opposite reaction. You have set up compiler config, you are changing your code for that compiler config, now the compiler is breaking your JavaScript and thus changing your runtime behaviour. Thats exactly why everybody wants these paths rewritten. Such a simple change, with so much fuss, is really a shame.

Our position is that you should write the path that works at runtime, and set up your config however is necessary so that TS can understand what those paths resolve to. What's being requested is the opposite: that the developer writes a path other than the one that works at runtime, and that the config would tell TS what path to write instead. That's exactly what's being proposed in this comment, for example.

It seems like what several people in this thread have done is:

  • Start with working relative paths
  • Add path mapping to their tsconfig to invent new module paths that don't work at runtime (e.g. @Component/foo)
  • Change their files to import from these fictional paths
  • It doesn't work at runtime because the runtime path is invalid
  • Feature request: TS should resolve my fictional paths for me

The correct solution is to just stop at step 1 where everything worked and you were writing the path you wanted at runtime! Which seems to be exactly what you're saying in your comment.

Am I taking crazy pills here? "TS doesn't have its own internal symlinking system" is a valid observation but it's not the case that we're trying to break your program by forcing you to use an invented set of fictional paths that never worked in the first place.

The only situations that it breaks behaviour is when you never had a valid behaviour to begin with. It just isn't magically fixing it for you, and that upsets you.

I had a whole thing written here, but @RyanCavanaugh has put it much more succinctly.

You are correct, this is what most people have done here, because paths are primed for this. Everybody who doesnt want to use the awfulness that is most tools that surround JS compilation at this day and age just want to compile with TS directly, and would just love paths, and then there they are, staring you straight in the face but refusing to take the last and easiest step. And the first time you notice, is after you have made it all compile.

And frankly, clearly this is how _many_ people not only want to use this feature, but intuitively assume that this is exactly what it is for.

I wouldnt say you are taking crazy pills, but why not simply enable this trivial feature, when there is so little downside? That seems crazy to me!

But lets get to @kitsonk`s suggestion:

After writing all this i realised that i have basically rewritten a proposal from way up in the thread, but hopefully in a clearer way, with an added example.

I will say that I have a little experience with the complexities of compilers and compiler options for programming languages, but nowhere near the amount you guys have, obviously. But i completely fail to see how this proposal add any significant complexities to thinking about the language or the compiler, as mentioned here

And while I dont condone the tone people have taking in the later portions of this thread, I can understand and share their frustration that a simple and easy to implement proposal is getting such blanket refusal, on what, respectively, seems so very little reasonable grounds.

Anyways, here goes nothing.

A simple proposal

Disclaimer

First off, I have not seen(which is not to say it doesnt exist) any reasons why this would not work, so im outlining the most simple and least intrusive thing I can think of to make the most basic case work nicely that would cover what I think is 99% of what the people in this issue yearn for.

The import paths can already be resolved internally in TS, otherwise it could not type check, which means the logic for checking wether an import path has a matching rewrite in the tsconfig is there, and working.

Terms

  • ResolvedPath: A path generated by an entry in paths that has matched a path in an import statement

Changes

Add a singular flag --rewritePaths which changes emitting behaviour such that an import statement with a ResolvedPath is using that path for emitting. It should change .ts file endings to .js.

It defaults to false.

All other behaviour remains untouched. If no paths are set, the flag becomes an effective no-op. This is only in effect if outDir is being used of course, if everything gets turned into a single file, imports are not relevant.
This behaviour should not be impacted by any other compiler config. AFAICT.

Example

All following file paths are of course relative to the --baseUrl.

Assuming paths to look like this:

"paths": {
    "helpers": ["helpers/index.ts"]
}

This file, located at "library/complicated/deep/path/source.ts"

import { helper } from "helpers"
helper()

should emit as

import { helper } from "../../../../helpers/index.js"
helper()

if paths was empty, it would emit as

import { helper } from "helpers"
helper()

Please let me know about any issue or oversight, I'm happy as can be to have productive discussion about this.

Also a final disclaimer: it is rather late over here in germany, so forgive me for typos or bad phrasing.

@RyanCavanaugh You are not taking crazy pills, but I think the hangup is here:

Add path mapping to their tsconfig to invent new module paths that don't work at runtime (e.g. @Component/foo)

It does work at run time if you're running with ts-node, which is a fairly common way of running TS code locally before shipping to a deployed environment, where it's compiled and no longer works. I can't really think of another place with TS compilation where the compiled code cannot execute because of runtime errors that otherwise don't occur when using ts-node -- maybe if you set the target too high for a node or browser environment?

@kitsonk I don't think these comments are totally fair given some of the previous communication from the TS team:

Being a complete build tool is not a goal of TypeScript.
tsc is not a bundler, never will be a bundler (or a full featured build tool)...

From this blog post:

Using the TypeScript compiler is still the preferred way to build TypeScript. While Babel can take over compiling/transpiling – doing things like erasing your types and rewriting the newest ECMAScript features to work in older runtimes – it doesn’t have type-checking built in, and still requires using TypeScript to accomplish that. So even if Babel builds successfully, you might need to check in with TypeScript to catch type errors. 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.

I don't think it's unreasonable for folks to expect that tsc is the only thing they need to build and deploy code. If the TS team strongly disagrees, I'm not sure they've sufficiently marketed that idea.

Then make a proposal that would work. A proposal would include what gets emitted and the logic. People so far have only proposed that some sort of magical flag be added to the configuration that will read the developers mind and emit something, but haven't expressed some sort of valid emit.

I'm not sure I understand what's problematic with this example: https://github.com/microsoft/TypeScript/issues/26722#issuecomment-524405693. I added a tsconfig.json-- you mentioned you weren't sure how the @common would be resolved, sorry I had included that part in a previous comment.

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

import a from "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.

I think at this point it's obvious that we have a philosophical problem, and NOT a technicial one. : )

In fact, my technical proposal has been sitting here for 3 months now receiving tens of upvotes from users. In the meantime, the third-party solution for this exact issue has passed 500,000 downloads per week earlier this month!


It seems like what several people in this thread have done is:
• Start with working relative paths
• Add path mapping to their tsconfig to invent new module paths that don't work at runtime (e.g. @Component/foo)
• Change their files to import from these fictional paths
• It doesn't work at runtime because the runtime path is invalid
• Feature request: TS should resolve my fictional paths for me

The correct solution is to just stop at step 1 where everything worked and you were writing the path you wanted at runtime!

I am sorry @RyanCavanaugh, but I smell the same dictatorship that another user mentioned in this thread earlier. I don't think when there is such a simple request, with millions of downloads for an alternative per month, you can tell what people _fictionally_ WANT is WRONG. That's not how technologies grow and gain popularity.

Putting all the details behind, developers simply want to have absolute paths in their imports instead of a train of ../../../.. that breaks so many things if you move a folder. That's as simple as it.


All being said, what we have heard so far is:

Case A) People shouldn't do it

I'm sorry, just because you are in charge of official team behind an open source project it doesn't mean you can dictate people what they are allowed to WANT, if it's a valid and sane popular request. (Reminds me of the same stance Microsoft had in early 90's, which led to the birth of Open Source and Linux!)

Case B) It doesn't fall under the responsibilities of tsc

I'm sorry, but tsc already handles it during compile/type-checking time very well using paths. In fact, as @dannycochran mentioned above, if you guys want developers to rely on tsc as the preferred way to build, please make life easier for everyone.

Otherwise, the brand behind TypeScript would become "_there is an official tsc compiler, but it doesn't include what it takes to turn your working *.ts to working *.js flawlessly, unless you download this and this and that third-party package._"

Case C) Oh, if we do this, then we need to do that, and rewrite half of the compiler

No! All people, and I am talking about millions of people, want is SUCH A SIMPLE CAPABILITY TO BE ADDED UNDER AN OPTIONAL FLAG. Optional means (I think I ought to mention it) that it's going to be off/disabled by default. So unless someone actually goes and turns this new knob, it's not going to do anything. Thus, not a breaking change.

We can sit and argue for eons, but at the end of the day, Done is better than Perfect! And here, undone (plus the attitude we are seeing here) is hurting the reputation of a growing technology.

Thanks.

PS.

Here are some questions I have about this feature:

I am not sure if these are questions to improve the comprehensiveness of the propsal, or just to show off that oh it's difficult/unwanted-by-the-official-team.

Let's stay positive, anyway! Instead of going into the weeds, I can tell that the User Story is as simple as:
_I have @model/*" : [ "src/model/*" ], under paths in my tsconfig.json. I just want any import userData from "@model/user/data" to work as expected from anywhere in my codebase. Currently it works during compile-time but doesn't work during run-time, as you know._

I bet this is 98% of what people (at least those millions of customers of the third-party) want. It's up to you to decide whether these 98% customers should wait until we get the answer to all the little details of what the other 2% want or not.

Thanks for your time anyway.

All being said, what we have heard so far is: ...

Many of those things have not been said.

Let me try to be as clear as possible:

  • TypeScript doesn't change the behavior of JavaScript code.
  • import statements are JavaScript code.
  • Therefore TS is not going to modify that statement.

It's not even about scope, it's about one of the ten things we said we will never do, and we intend to keep not doing it. People have told me dozens of times that we need to violate those principles for their particular feature or else TypeScript is doomed to failure.

I am not sure if these are questions to improve the comprehensiveness of the propsal, or just to show off that oh it's difficult/unwanted-by-the-official-team.

People keep shouting about how this is trivial, therefore must be done, as if we're afraid of doing the work. It may be simple to implement, but that doesn't mean it's conceptually straightforward, and ultimately we care a lot more about the conceptual load of a feature that our users have to endure than the difficulty of putting together a PR to add the feature.

Wouldn't it also conceptually conflict with import maps?


Here are some questions I have about this feature:

Another one I'd like to add is handling of dynamic import syntax. Should they be transformed as well? If yes, that might present a very surprising behavior:

import('path'); // works
const path = 'path';
import(path); // fails

If no, that might be against the specification, since both static and dynamic imports use the same abstract operation for specifier resolution.

Wouldn't it also conceptually conflict with import maps?

As the paths feature stands now, it reasonably models import maps. It is how the feature of SystemJS and AMD was standardised in a browser context. It doesn't conflict conceptually any more because of it, but it underlines that TypeScript doesn't want to get into the business of re-writing module specifiers, because that is effectively a runtime only concern.

As I mentioned before the solution would be an overhaul to the plugin
system. Another pain point which the community has complaining about for
years now. It would save a lot of headaches fro you guys if we would be
able to customize the compiler on our own through plugins and it would
easily tackle such cases as this one.

On Fri, Sep 20, 2019, 04:04 Kitson Kelly notifications@github.com wrote:

Wouldn't it also conceptually conflict with import maps?

As the paths feature stands now, it reasonably models import maps. It is
how the feature of SystemJS and AMD was standardised in a browser context.
It doesn't conflict conceptually any more because of it, but it underlines
that TypeScript doesn't want to get into the business of re-writing module
specifiers, because that is effectively a runtime only concern.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/microsoft/TypeScript/issues/26722?email_source=notifications&email_token=AAHLJQGE4HWI2OCOPAL6HU3QKQVUPA5CNFSM4FSCEI7KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7FKBWY#issuecomment-533373147,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAHLJQEH4Q6YKFQ5RXLVEC3QKQVUPANCNFSM4FSCEI7A
.

+1

I think this is really important, as TypeScript declarations for libraries are essentially broken if you use absolute paths. We then need to manually fix them, or use a tool to go through and correct paths.

If my library internally imports ./src/folder as folder (which is the same as ./folder), this works fine. I can use Rollup, Webpack, etc. to update the paths as appropriate.

But I can't do the same for d.ts files. The result is that TypeScript (in a _user's_ project) thinks my library is importing a package called folder and fails.

For the JavaScript itself, I understand the argument for not changing output.

I spend all day trying to figure out what wrong with my setup and why paths aren’t resolved in outputted js files. Makes this feature almost useless and very confusing, since it processes NOT WORKING output for NodeJS. Please add some option to resolve paths that tsc already knows internally and bring the end to this struggles. It wouldn’t break anything if it will require option to enable resolution.

I also ended up here after searching for why my emitted code doesn't have the paths I expected. After reading through this thread, I think I agree with what seems to be the unpopular position of not changing the paths in the output. There's just too much to consider, and I don't see how tsc could possibly account for every variation of build process out there, not to mention things like automated tests, etc.

What I would suggest to the tsc devs is to make the documentation a little clearer here: https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping

Right now, that section could be interpreted to mean that emitted code will have the paths resolved, but really the setting just makes it so that we can use other tools for module resolution and allow TypeScript to understand what those imports mean. The documentation not being clear about that seems to have led to some confusion and expectations among a lot of users, myself included.

There's just too much to consider, and I don't see how tsc could possibly account for every variation of build process out there, not to mention things like automated tests, etc.

@mrobrian what is suggested here multiple times (and has received many upvotes and support from other developers) is to add an optional flag that supports the simple absolute paths to relative conversation in the output, just as it's already doing in the compile time.

The two bold words here are:

  • Optional, so it wouldn't break anything. Only people who want it will use it as an experimental flag.
  • Simple, as simple as what https://github.com/dividab/tsconfig-paths does. I understand we can make a beast out of this to support 100% of the use cases; but just providing the simple 20% of it will satisfy 80% or more of the needs.

Thanks.

+1

Just gave up and change 72 file in one of my packages. Moved back from module path to relative path imports.
IT LOOKS SOOOO UGLY - All those ../ It makes me wanna puke!
It is tilting at windmills - I'm broken. MS you won - Congrats! :cry:

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

import a from "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.

@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:
{
  "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:

  • 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.

It also achieves what we ask for using babel & babel-plugin-module-resolver (just in case anyone looks for alternative solution):

  • install @babel/cli & babel-plugin-module-resolver

    • yarn add --dev @babel/cli babel-plugin-module-resolver

  • Create .babelrc file 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"
                }
            }
        ]
    ]
}
  • Build your project by: yarn tsc -b && babel dist -d dist

    • This assumes you output all build files to dist directory.

    • It will, firstly, compile your project using typescript compiler tsc

    • Then, pass all compile result in dist directory through babel and replace the module name and output back to the same dist directory

Unfortunately, babel won't touch *.d.ts files in the dist directory.

It will be good that this feature can be implemented by tsc or make it possible to create a plugin of tsc to do so.

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.

Yes, https://www.npmjs.com/package/tscpaths
UPDATE: Sorry, linked wrong package first.

@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.

The author did a limited implementation just to support the basics, but I
think it'd be easy to add support for extends. Make a PR if you have time.

On Wed, Jan 8, 2020 at 5:00 PM Tom notifications@github.com 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
https://github.com/microsoft/TypeScript/issues/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:

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

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.

If path maps are not resolved into emitted code, then what are they for?

@winseros this heavily downvoted comment I wrote a few months ago answers that question. https://github.com/microsoft/TypeScript/issues/26722#issuecomment-516935532

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jbondc picture jbondc  Â·  3Comments

MartynasZilinskas picture MartynasZilinskas  Â·  3Comments

bgrieder picture bgrieder  Â·  3Comments

remojansen picture remojansen  Â·  3Comments

siddjain picture siddjain  Â·  3Comments