-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Feature Request: allow change file extension of generated files from .ts
#49462
Comments
What are exact use cases and what problem it should resolve? |
Major use-case is building dual-package with |
You shouldn't be dual building packages anyways (use a wrapper), so I prefer the |
i don't wanna make files of .cts and .mts they are same context |
I've met same issue when I building dual package. e.g. https://github.com/azu/check-ends-with-period/tree/v2.0.0 (It is invalid example as dual package)
I've defined
I could not found just works solution without using bundler/post scripts. If
However, this option will need to rewrite import path of source code. It oppsite TypeScript's design goal. ... Edit(2023-01-14): I've created tsconfig-to-dual-package for avoiding this issue. |
Might I suggest packemon: https://packemon.dev/ Also, why exactly are you dual building? You run the risk of the dual package hazard: https://nodejs.org/api/packages.html#dual-package-hazard It's better to use an ES module wrapper. |
i think this only work on nodejs
|
Browsers don't support .cjs/.mjs natively, unless it gets bundled through webpack or a similar tool to .js, and at that point, why even use .cjs/.mjs for browsers? |
The idea is to use native modules when targeting browsers without any transpilation/bundling, and optionally CommonJS when targeting Node. |
Yes of course, but not if you're using .mjs. At least in @azu's example, their ESM code should be shipped to the browser with .js, and CJS code to Node.js with .cjs (or even just .js too). We also just need more information, as we're making many assumptions here. The original post doesn't contain much. |
Wouldn't we be emitting |
I'm pro @bluelovers's suggestion. The use case for me is publishing dual ESM/CommonJS packages for nodejs. There are many reasons why we need to publish dual ESM/CommonJS packages. For instance, not until 4.7, TS doesn't even allow a node application in commonjs to consume a pure ESM library due to the lack of support on |
@milesj the use of an ESM wrapper around CommonJS defeats tree-shaking, doesn't it? Given ESM has broken the whole ecosystem to try and achieve results like tree-shaking, having the recommended way to align with ESM being to throw away its core features is disappointing. Correct me if there is a reasonable way to get both. My expectation is to follow the principle that modules should be stateless - a good practice I don't ever find the need to violate. Then I understand there are no concerns with dual building. |
If you have a dual package, and some other package in CJS context requires your package, and another package in ESM context imports your package, you'll end up with 2 copies of your package. For node this doesn't matter too much unless there's some kind of global/shared state, but for bundlers this is bad. |
@azu @milesj It looks like that tsc-multi might be worth exploring cc @tommy351 #18442 (comment) |
I've created tsconfig-to-dual-package for avoiding my issue that is described in #49462 (comment)
In other words, Both(CJS and ESM) are $ tsc -p . && tsc -p ./tsconfig.cjs.json && tsconfig-to-dual-package
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# \ I want to remove it! This approch pros is that no require addtional build/transpiler tool(no modify output code of 📝 Note TypeScript may change the moduleResolution, but did not likely change the output. I feel like there is confusion everywhere about ESM support. Therefore, I thought that this is not an issue of TypeScript configuration, but rather an ecosystem-wide issue that needs to move forward. |
|
I don't get the "you shouldn't be dual building" opinion, clearly there's a lot of fragmentation in the ecosystem currently and while everyone's moving in the direction of using ESM, there's still a lot of CJS projects and that's not going to change overnight. Because of this reason if you're building a library, it's important to output both in CJS and MJS. Additionally, Thus, it makes perfect sense to me that instead of outputting results with a vague ".js" extension which causes issues and headaches, you'd want to have explicit .cjs and .mjs extensions for CommonJS and ESModule modules respectively. On top of that, outputted declaration files should have d.cjs and .d.mjs extensions respectively, cause another issue I've ran into multiple times recently is library authors outputting results in .mjs and .cjs, but leaving the declaration files with the .d.ts extension which TypeScript then won't be able to pick up on. |
What are people currently using to work around this? Separate build steps with manual file extension rewriting? |
I believe that Dual Package can be achieved broadly as follows.
|
I stumbled upon this issue recently while wanting to build a dual package using babel and typescript. This file extension proliferation in the JS ecosystem is, needless to say, frustrating. I just started a project Running |
What we ended up doing is simply... not supporting ESM. In my eyes, the ESM ecosystem is simply not robust enough and we were spending more time trying to fight through it than actually solving real problems. Maybe once the tooling is there, but it's simply not. I want to be able to import using barrels like |
To add my two cents, regardless of whether you want to emit dual variants, At the moment, if you write your source code as |
If you want to update your ESM/CJS specifiers pre/post build, check out @knighted/specifier. It will parse a file and update specifiers using a provided callback or regex map. Then write the updated source to disk using whatever file extension you want. You can also try @knighted/duel (which uses @knighted/specifier) to easily create a dual CJS build. Here's an example repo, which is using the default args of |
The fact that See #54573 and all the related issues mentioned in this comment. The file extension nonsense is annoying, but that's how we let the two module systems that Node currently supports coexist. |
Today I also had the need for compiling
|
I need this nowadays, but we may not need it in the future, so keep it silence. Let time forget it. |
I would tweak this a bit so that you get |
I think In my understanding your tweak, as you call it, would introduce a slack rule unconditionally evaluating Anyway, other than few thumbs up, I don't see any reaction that suggests the general experience will be improved any time soon. |
@RyanCavanaugh because many people use the default I'm also pretty sure @azu's I haven't seen any TS dual-package authoring tools that seem unproblematic. The popular If |
The whole reason we don't want to do dual-emit is that there doesn't seem to be an unproblematic way to do it. What can TS do that they can't? |
If you set |
@RyanCavanaugh what makes general-purpose tools for dual-emit problematic is they have to either copy the source code to a temporary directory and codemod the import path extensions before compiling, running the risk of something breaking in the temporary location, or they have to destructively modify the source code in place before compiling. Only I use a custom And it's hard to make a majority of the community aware of 3rd-party tools for this the way Modifying the import path extensions is really simple compared to most of the transpilation that And if @morganney are you talking about having both |
No, I’m talking about a build tool that runs tsc twice while updating the type in package.json and rewriting specifiers. Use whatever file extension you want. Which is more or less what you’re saying. The biggest hurdle to this being baked into tsc is that they refuse to rewrite specifiers. Hopefully with the ability to |
Right, there are edge cases (e.g. subpath imports) you can only support by rewriting specifiers, and doing that inside
If I did my research, this still requires an command line flag in Node 22, which doesn't reach end of life until April 2027, so if we want to support all users of LTS Node, we still have to dual build for another 2 1/2 years. The choice between being conservative or helping the JS ecosystem run more smoothly for those 2 1/2 years is in the TS developers' hands. |
Suggestion
🔍 Search Terms
List of keywords you searched for before creating this issue. Write them down here so that others can find this suggestion more easily and help provide feedback.
✅ Viability Checklist
My suggestion meets these guidelines:
⭐ Suggestion
add something like
📃 Motivating Example
targetExtension
is.cjs
, all.ts
will emit as.cjs
, but.mts
still is emit as.mjs
targetExtension
is.mjs
, all.ts
will emit as.mjs
, but.cts
still is emit as.cjs
💻 Use Cases
The text was updated successfully, but these errors were encountered: