Typescript: Publishing to NPM while using the tsconfig paths?

Created on 16 Jul 2018  Β·  15Comments  Β·  Source: microsoft/TypeScript

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?

Out of Scope

Most helpful comment

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.

All 15 comments

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.

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

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.

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

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.

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.

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.

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

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?

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

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

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

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?

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

Was this page helpful?
4 / 5 - 1 ratings