Closed CarlosNZ closed 8 months ago
Indeed, it would appear I'm not the only one. This is what I get when I try to look up the package in Bundlephobia:
https://bundlephobia.com/package/change-case
This is fine: https://bundlephobia.com/package/change-case@4.1.2
It’s a duplicate of https://github.com/blakeembrey/change-case/issues/304. Unfortunately Bundlephobia doesn’t support ESM so you can’t rely on that result. Please see the note in the READMEs of the packages on how to enable ESM, I don’t support CommonJS anymore.
I also have this issue with an angular 16 project. Changing the moduleResolution to node16
, esmNext
, or bundler
, breaks all of our imports.
If you have a reproduction I can look at I can help you debug, but that sounds more like a bug in angular. Typescript support for ESM is just following https://www.typescriptlang.org/docs/handbook/esm-node.html
Might also be a problem with moduleResolution
being set to node
in tsconfig in the projects trying to use change-case. If you’ve got a recent version of typescript one could probably switch to bundler
instead and keep approximately the same behaviour, but importing change-case would start working, it worked when I tried it out.
This is because change-case package.json doesn’t include a module field pointing to the dist folder, and ts projects still using node as moduleResolution will fail to find the types. They don’t seem to know anything about the exports field.
change-case could add it, it won’t do any harm to the project I guess, and it would work for those old typescript projects as well, probably a lot of projects not changing the moduleResolution that often. Or perhaps hint at the solution in the docs for anyone trying to figure it out?
I noticed it in a project that uses a lot of other esm-packages, but types for change-case didn’t work.
I've found that I needed to do three things to get it just about working:
'moduleResolution': 'bundler'
. All others fail.'type': 'module'
in the package.jsonHowever all of our tests now fail because Karma doesn't support ESM imports in their conf.js and ESM doesn't support requires
. We're also not too comfortable changing the moduleResolution
unless we can guarantee the same behavior and only using change-case for case insensitive splitting in a few places, so messing with the moduleResolution` and all the changes that brings seems a little excessive when we could just split the strings another way.
We use Vuex 4 which doesn't support 'moduleResolution': 'bundler'
either, so back to pascal-case it is for us :(.
@blakeembrey this esm change breaks my entire nx monorepo...
@JakeAi No it doesn’t. You aren’t being forced to upgrade. That’s the point of a breaking change version bump, it introduces incompatible changes with the previous version. I no longer intend to maintain CommonJS packages.
change-case could add it, it won’t do any harm to the project I guess, and it would work for those old typescript projects as well, probably a lot of projects not changing the moduleResolution that often. Or perhaps hint at the solution in the docs for anyone trying to figure it out?
No, adding it won’t fix typescript. I’d need to create a CommonJS compatible version of the package, therefore shipping two versions. I did that for the last few years with the previous major version, going forward it’ll be ESM only.
Also, there is documentation added to each of the packages READMEs, can you let me know if that is insufficient?
I create a blank nx repo, with an angular app. Import your library. And change-case package is not found. I add "module": "node16", "moduleResolution": "node16"
and angular libraries break and I get warned about the current file being CommonJS. I add "type": "module",
and I get
./apps/mm-site/src/app/nx-welcome.component.ts:8:22-44 - Error: Module not found: Error: Package path . is not exported from package C:\Development\mm\node_modules\change-case (see exports field in C:\Development\mm\node_modules\change-case\package.json)
./apps/mm-site/src/app/nx-welcome.component.ts:9:40-67 - Error: Module not found: Error: Package path ./keys is not exported from package C:\Development\mm\node_modules\change-case (see exports field in C:\Development\mm\node_modules\change-case\package.json)
I’m sorry, it’s ESM only now and apparently that doesn’t work properly with Angular. I’d suggest filing a ticket with them, I’m not the only person building packages with ESM nowadays.
change breaks my entire nx monorepo
I think I misinterpreted this to assume you meant an existing repo. Of course, you can still install the previous major release. You don’t need the latest version.
@JakeAi If there’s anything you can share with me to reproduce I can file an issue on your behalf if you’d prefer. I’m not familiar with NX or Angular.
It could be related to something like this? https://github.com/nrwl/nx/issues/18801. Or maybe this? https://github.com/nrwl/nx/issues/15464.
TL;DR is that it seems like NX is doing some sort of “magic” that just doesn’t work properly with ESM + TypeScript here. Unfortunately I’m not too familiar with this, I typically avoid using tools like this and use direct tsc
and/or a simple bundler like ESBuild.
Also https://github.com/nrwl/nx/issues/11335, seems like it’s been over a year and people can’t run ESM using NX 🤷 It doesn’t sound promising.
change-case could add it, it won’t do any harm to the project I guess, and it would work for those old typescript projects as well, probably a lot of projects not changing the moduleResolution that often. Or perhaps hint at the solution in the docs for anyone trying to figure it out?
No, adding it won’t fix typescript. I’d need to create a CommonJS compatible version of the package, therefore shipping two versions. I did that for the last few years with the previous major version, going forward it’ll be ESM only.
No. I was not referring to the ESM switch. I was referring to adding the module field in package.json which would fix this issue for all projects using a tsconfig with moduleResolution set to node which probably is a lot. When set to node ts don't understand the exports field in package.json according to my understanding. Which gives the problem described in this issue. I had a project using multiple ESM packages and it's an ESM itself. But this issue was still present due to the missing module field in package.json pointing at the dist folder. If you don't want to add it for some reason maybe it could be documented as a troubleshooting tip to others having the missing ts types issue to check if it's possible to switch moduleResolution setting?
I was referring to adding the module field in package.json which would fix this issue for all projects using a tsconfig with moduleResolution set to node which probably is a lot.
This doesn’t fix typescript, it’s not how typescript works. How did you get this to work?
There’s nowhere in TypeScript’s module resolution strategies that it even uses the module field, as far as I’m aware. Some bundler may consider it, but what TypeScript actually uses in the old node module resolution strategy is the types/typings field. And that is expected to be the CommonJS version of the package. If I added that back, it wouldn’t work properly with imports because it’d allow CommonJS in TypeScript to work differently depending on the users synthetic import options. And then I’d just have a different set of issues being filed asking for CommonJS to work.
My project still doesn't work, it's been a whole day trying to fix this. :(
There’s nowhere in TypeScript’s module resolution strategies that it even uses the module field, as far as I’m aware. Some bundler may consider it, but what TypeScript actually uses in the old node module resolution strategy is the types/typings field. And that is expected to be the CommonJS version of the package. If I added that back, it wouldn’t work properly with imports because it’d allow CommonJS in TypeScript to work differently depending on the users synthetic import options. And then I’d just have a different set of issues being filed asking for CommonJS to work.
Don’t know, might have changed something else aswell.
I’m not an expert at this but the module resolution thing in ts is an issue when only exports
field is available in the package.json, since many of the settings doesn’t seem to work.
Tried in a mini repro:
package.json
{
"type": "module",
"devDependencies": {
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
},
"dependencies": {
"change-case": "^5.0.2"
},
"scripts": {
"start": "ts-node-esm index.ts"
}
}
tsconfig.json
{
"compilerOptions": {
"target": "es2016",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
index.ts
import * as changeCase from "change-case";
console.log(changeCase.camelCase("what is happening"));
This above e.g. will say types are missing.
Changing module
and moduleResolution
in tsconfig to:
"module": "NodeNext",
"moduleResolution": "NodeNext",
Will make it work.
And manually editing change-cases package.json and adding:
"types": "dist/index.js",
Will also make it work.
There might (and are) more ways to make it work. I used another combo in our bigger project.
But since there seem to be a lot of tsconfig settings preventing typescript from finding the types when only exports
is available in the package.json it might be an easy solution to add e.g. types
or typesVersions
field(s) or something else helping more moduleResolution settings finding the types. :) without signaling anything about commonjs.
Some other esm packages seem to have both ”types” and ”exports” e.g.: https://www.npmjs.com/package/@sindresorhus/is https://github.com/sindresorhus/is/blob/main/package.json
@emanuel-lundman Thanks for the references. FWIW I wanted to check why they added types
since it's technically incorrect for TypeScript and he's saying the same thing here: https://github.com/sindresorhus/is/pull/180.
Specifically:
I will revert it for now. But it will come back in the next major version.
The problem is your TypeScript config. Make sure you'are on TS 5 or later and use this config: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c#how-can-i-make-my-typescript-project-output-esm
(The gist link is the same one I've added to the package READMEs to clarify this too).
I don't quite follow if we're talking about the same thing. But that's alright. Thanks for looking in to it. 🙂
Just to be clear, I wasn't saying there's a need to revert the exports field in package.json to make it work. But adding the types field seemed during my test to make it much more compatible with more moduleResolutions than node16.
Switching to moduleResolution node16 can be a big hassle for an old large project. Adding extensions to all imports maybe in hundreds of files etc. And seems like typescript still promotes to add it to the package.json. https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html
TLDR;
Adding types
field to package.json seems to me like it could improve compatibility and make typescript find the types in more situations and it's such a minor thing.
But of course, my testing hasn't been that exhaustive.
The package.json I referenced in the sindresorhus package e.g. was for the next major version (6.0.1) and he had reverted to using exports
again but he still had a types
field in his package.json.
...
"type": "module",
"exports": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"node": ">=16"
},
...
But of course it's up to you to decide. Wish you all the best. 🙂
But adding the types field seemed during my test to make it much more compatible with more moduleResolutions than node16.
Yes, I agree. It works for TypeScript, but it won't work for node projects using CommonJS. I vaguely understand wanting to allow people to just upgrade and it work, but to be fair it'd just break elsewhere. I.e. the error would turn into a node.js issue trying to require
an ESM package. Arguably it's "better", but I'd personally prefer to contribute to upgrading the NPM ecosystem than comfort. And we have found and can fix the TypeScript compiler message to improve this experience now too.
was for the next major version (6.0.1) and he had reverted to using exports again but he still had a types field in his package.json.
It doesn't look like he reverted again, just forgot to revert before 6.x 🤷 Easy to forget I suppose, but I see what you mean now. I guess there's other reasons he might have left it alone, I've found other ecosystem interop issues too (i.e. NPM package pages also don't support ESM types exports).
I'll put my effort to improving the ecosystem rather than hacking it in this package, hopefully we can make it so this is less confusing on future TypeScript versions.
@emanuel-lundman I thought more about your point overnight and added types
back for all the packages. Hopefully it alleviates some of the TypeScript pain, but it doesn't add CommonJS so it may introduce a different set of confusion. I'll make a backlog of the issues flagged from this about the ESM ecosystem and maybe myself or someone else can help improve the tooling:
Cannot find module 'change-case' or its corresponding type declarations.
https://github.com/microsoft/TypeScript/issues/55104In another issue someone shared this and it was very helpful way to debug TypeScript/ESM support: https://arethetypeswrong.github.io
Package has been working fine with v4.1.2
However, after upgrading to v5, I get the following error:
My import command is just
import { camelCase } from 'change-case'
, which worked fine before. 🤷‍♂️