Skip to content
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

Publishing to NPM while using the tsconfig paths? #25677

Closed
oleersoy opened this issue Jul 16, 2018 · 18 comments
Closed

Publishing to NPM while using the tsconfig paths? #25677

oleersoy opened this issue Jul 16, 2018 · 18 comments
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints

Comments

@oleersoy
Copy link

oleersoy commented Jul 16, 2018

Hello - Hope it's OK that I ask this question here. I first posted it to SO and have seen similar questions asked without answers. This is the SO question:

https://stackoverflow.com/questions/51353762/compiling-typescript-path-aliases-to-relative-paths-for-npm-publishing

In addition I created a small project with a directory structure like this:

├── package.json
├── src
│   ├── boo.ts
│   └── foo
│       └── foo.ts
├── target
│   ├── boo.d.ts
│   ├── boo.js
│   └── foo
│       ├── foo.d.ts
│       └── foo.js
└── tsconfig.json

I setup the `baseUrl` and `paths` option like this:

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

In this case `boo` imports `foo` and when compiled the import looks like this:

"use strict";
exports.__esModule = true;
var foo_1 = require("@fireflysemantics/foo/foo");
function boo() {
console.log("The boo is loose");
foo_1.foo();
}
exports.boo = boo;


So as you can see the `require` is still importing from the @fireflysemantics alias defined in the `tsconfig`.  However when published to NPM, node does not necessarily know where that is.  Thoughts?
@kitsonk
Copy link
Contributor

kitsonk commented Jul 16, 2018

This is not a support forum.

Questions should be asked at StackOverflow or on Gitter.im. Cross posting from StackOverflow is also not a good idea.

@oleersoy
Copy link
Author

oleersoy commented Jul 16, 2018

@kitsonk as you can see in the question I did first ask on SO. There are also other people that have asked. One of the reasons I have asked it is I wonder if it's essentially a bug? In other words we can do what articles like this say that we can do. But once we go to publish to NPM, we effectively have to undo it all and go back to relative paths.

So in other words since Typescript compiles the path that the typescript compiler and ts-node know how to interpret via tsconfig.json into the es5 source output, how is node supposed to interpret the fixed import path now (since it has no knowledge tsconfig.json)?

Also asked a follow up question on SO just now here.

Thoughts?

@ghost
Copy link

ghost commented Jul 16, 2018

TypeScript is a means of typing JavaScript code -- but the types just describe existing behaviors, they don't change them. The declarations in "paths" in tsconfig.json should be an accurate reflection of the actual behavior of your module loader. If your module loader doesn't support global paths in that way, you shouldn't declare that it does.

@ghost ghost added the Out of Scope This idea sits outside of the TypeScript language design constraints label Jul 16, 2018
@oleersoy
Copy link
Author

TypeScript is a means of typing JavaScript code

That's one aspect of it. I would say the biggest driver and rationale for Typescript is to make Javascript as easy to refactor as say Java within an Eclipse IDE, and if this aspect of it remains as is we are going to see a lot of confusion moving forward.

Typescript could easily support a configuration flag for generating relative paths for NPM published modules that way we could code in pretty non relative paths, yet have the ES5 generated code be usable in Node. It seemed so obvious to me that that was what the path configuration parameter was for, that I did not even test the actual production of the NPM module before writing a fairly large project.

@kitsonk
Copy link
Contributor

kitsonk commented Jul 17, 2018

@kitsonk as you can see in the question I did first ask on SO.

You waited only a few hours before opening an issue. That is what I meant by cross posting. Instead of waiting for an answer on StackOverflow.

Typescript could easily support a configuration flag for generating relative paths for NPM published modules that way we could code in pretty non relative paths

This conflicts with TypeScript non-goals:

  1. Provide an end-to-end build pipeline. Instead, make the system extensible so that external tools can use the compiler for more complex build workflows.
  2. Add or rely on run-time type information in programs, or emit different code based on the results of the type system. Instead, encourage programming patterns that do not require run-time metadata.

It seemed so obvious to me that that was what the path configuration parameter was for, that I did not even test the actual production of the NPM module before writing a fairly large project.

I don't think it is a good idea to build large code bases without testing before you realised you did not correctly understand the option. The documentation for the option makes it pretty clear it is designed to allow mimicking of the behaviour of loaders like Require.js or System.js, not rewriting module IDs.

Also, this is a duplicate of #9910 and #10866.

@oleersoy
Copy link
Author

oleersoy commented Jul 17, 2018

You waited only a few hours before opening an issue. That is what I meant by cross posting. Instead of waiting for an answer on StackOverflow.

True but there were already other similar questions that had gone unanswered for months, so I figured I'd just try again to reaffirm what I already suspected was true.

Provide an end-to-end build pipeline. Instead, make the system extensible so that external tools can use the compiler for more complex build workflows.

Typescript already compiles to various Javascript module formats. I, and I'm sure others, were totally expecting the paths to be converted when compiling to the ES5/CommonJS format. Ts-node does this in memory.

Instead, encourage programming patterns that do not require run-time metadata.

Precisely. With the current state of things the only way to use the paths feature with the ES5/CommonJS output is to write an additional post processor for the code that Typescript has compiled.

All this does is force everyone that wants to use the paths feature to jump through additional hoops in order to get their code workable on NPM. Most documentation and tutorials on the paths feature do not mention that part at all, so as it is right now a good number of us are going to run straight into the same wall that I did.

At least have the Typescript compiler warn that the paths option and the ES5/CommonJS flags can produce non default resolvable Javascript files.

@oleersoy
Copy link
Author

oleersoy commented Jul 17, 2018

Right now devs are looking for the development features that the Say Goodbye to ‘../../../..’ in your TypeScript Imports describes. Because we all like easy to read and refactor imports.

Node does require relative paths in order to resolve modules, but Typescript appears to have solved this. Awesome. This is why we love Typescript. It's worth the learning curve investment because it has awesome features like this.

The article goes on to say:

Wouldn't it be great if you could forget about relative paths entirely?

Yes that would be really great. If we can use absolute paths that are easy to refactor what's not to love about that?

The article gives a demo and goes on to say:

No matter where your file sits in the tree. It Just Works.

SWEET!

But guess what, the most common scenario for using and publishing your Javascript once the project is complete is not supported. I'm pretty sure Typescript will get a lot more frustrated user comments around this down the line.

@oleersoy
Copy link
Author

oleersoy commented Jul 17, 2018

So it appears that there is a solution module-alias which has 10,000 weekly downloads. So 10,000 devs are having to perform additional weekly to configure something that should be part of the compilation process. However it does not appear that this works with NPM packages that are built to support other TS projects (*.d.ts and corresponding *.js files), so it's back to the drawing board.

If we are compiling to CommonJS / ES5 format there is essentially one target runtime, Node. So we expect the compiled code to resolve properly in that runtime.

@oleersoy
Copy link
Author

I think this could be this simple.

  1. Pick a symbol for the the root of the import path like @example.
  2. Understand what the root of the compile output is ( For example ./dist/);
  3. Understand where a particular source file is located ( For example `./dist/foo/boo.ts)
  4. Count the number of /s in the path
  5. If the count is 2 then just replace @example with ./.
  6. If the count is greater than 2 then replace @example/ with Array(count-2).fill('../').join('')

And now we have a set of *.d.ts files and *.js files that can be npm installed if we go to ./dist and run npm install from the client project. I'm still experimenting, but I have a feeling it's really this simple. I am missing anything?

So if tsconfig.json had a generate relative paths flag, and this feature was implemented in the compiler, then we have a solution ...

@oleersoy
Copy link
Author

I've implemented a script that converts all of my projects absolute paths to relative paths. After having done this it seems even more obvious to me that this is the right thing to do when compiling to ES5 / CommonJS format. Honestly what could the down side possibly be?

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@joonhocho
Copy link

Until officially supported, I've created a solution that replaces absolute paths back to relative paths 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!).
It's very easy as adding one line of script in your package.json.
https://github.com/joonhocho/tscpaths

@danielpza
Copy link

I made a transform for this, checkout https://github.com/LeDDGroup/typescript-transform-paths

@dlqqq
Copy link

dlqqq commented Aug 16, 2020

I think this issue was dismissed exceedingly unfairly; this is a very valid issue that me and many others are experiencing given the popularity of the ttypescript and typescript-transform-paths plugins. Many people depend on TS to write good code, and it is completely irresponsible and pointless to rudely lash out at issue creators looking to improve your project.

Is there any possibility that this issue can be re-considered?

@neodon
Copy link

neodon commented Aug 16, 2020

This issue seems simple: When you use the "paths" feature of the TypeScript compiler, it produces invalid output. That seems like a significant defect.

@AlanBlanchet
Copy link

Hello guys,

It's been a few days that I'm stuck on mapping my paths... I also believe like many others that there should be a better way of doing so.
I'm currently trying to export my "d.ts" with the path to my react components like so :
"@Components/" => "src/components/"

I am using webpack for this project and also have the webpack path aliases that map...

Anyways, I believe that in a node project the main paths should be in the package.json. Then the tsconfig should configure accordingly (I mean use the package.json paths). Thus every output (build) from this project should have its paths replaced by the absolute ones.

I spent too much time on this... It's sad to admit like many other that such a simple thing is not yet implemented. I'll have to find a workaround. Good luck to all of you !

@ghost
Copy link

ghost commented Oct 14, 2021

@kitsonk This issue seems to have caused a lot of friction. I'll give my best go at countering your argument against it.

This conflicts with TypeScript non-goals:

  1. Provide an end-to-end build pipeline. Instead, make the system extensible so that external tools can use the compiler for more complex build workflows.
  2. Add or rely on run-time type information in programs, or emit different code based on the results of the type system. Instead, encourage programming patterns that do not require run-time metadata.

This feature request is not in conflict with non-goal 4 since publishing TypeScript code to npm is a basic build workflow, not a complex one.

Non-goal 5 says we shouldn't emit different code than JavaScript just because a different TypeScript typing is there. Well, JavaScript emits code that is compatible with Node even with the use of import aliases. TypeScript is an existing offender to non-goal 5 by emitting incompatible code with the use of import aliases. In order to abide by non-goal 5, TypeScript needs to emit valid JavaScript code that is consumable by Node.

And if there was any doubt about the validity of this issue, please re-read non-goals 1, 2, and 7 for further clarification.

  1. Exactly mimic the design of existing languages. Instead, use the behavior of JavaScript and the intentions of program authors as a guide for what makes the most sense in the language.
  2. Aggressively optimize the runtime performance of programs. Instead, emit idiomatic JavaScript code that plays well with the performance characteristics of runtime platforms.
  1. Introduce behaviour that is likely to surprise users. Instead have due consideration for patterns adopted by other commonly-used languages.

Import aliases are used by most major IDE's and JavaScript frameworks. Was due consideration given to these import patterns? Just reading through it seems to have gotten brushed aside rather brashly.

@Azbesciak
Copy link

Kinda sad that at the end of 2022 we still have no official solution. I am trying to export my project's core code to library, where I extensibly use aliases, and what now? I need to do some postprocessing on my own because I trusted in the clean code? Come on.

Honestly, when I first saw the path aliases I was delighted. Now I feel like after getting married </3

@microsoft microsoft locked as resolved and limited conversation to collaborators Oct 24, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints
Projects
None yet
Development

No branches or pull requests

9 participants