I've been testing jsr with some packages. The experience was good in general but I would like to drop here my impressions, I hope you find them useful.
I used to avoid unnecessary typing in my TS code but seems that the new TypeScript checking for JSR (called FastCheck) is much more strict than the Deno default configuration (which was also quite strict). This made me think about one of the main flaws of Deno (in my opinion): The segmentation of the developer experience.
I had the same feeling before when Deno Deploy was released. Everybody assumed that you could deploy your Deno code until we realized that Deno Deploy is not the same as Deno CLI and doesn't support the same features. This difference between both runtimes has a reason (and it's understandable), but it breaks the expectation and introduces friction in the Deno ecosystem UX.
Something similar happens with JSR. The current Deno standards are no longer valid anymore, and publishing a package in JSR requires changing the code to adapt to new specifications. As with Deno Deploy, there are reasons for this change, just mention that is not good for DX.
I've already expressed my disagreement. I think this option introduces two problems:
With exports, you can remove the extensions of the files, and you can import modules like jsr:@std/path/basename
. It's ironic that JSR requires explicitly for TypeScript but not for URLs. The extension provides info about the format of the file and how it must be imported. Especially with the import assertions, it's not the same a JavaScript file as a JSON file or webassembly. If you don't see the file extension, you are not sure how to import it. Probably when Deno (hopefully) supports more file extensions (like CSS), this will be even more critical.
It's possible to export the files including the extension, but that's not my point. The point is JSR allows us to export them without extension, and even @std
, the official Standard library which should be the model to follow encourages that.
The ability to rename the file introduces another problem: the module resolution now is more obscure, and less obvious, which is harder for debugging. Now, when you import a file, you don't know which file is being imported, even looking at the source code of the package. You have to see the "exports" entry of the deno.json file to find the name of the original file.
One solution is to allow to specify the files of the public API but not rename them, by using an array instead of a key => value object.
JSR requires you to explicitly specify all files that you want to make public. In simple modules, it can be a single file, but other packages have tens of files.
I can guess the reason for this: If all files of the module are accessible, they are part of the "public API". This means any change in the file system (like removing or renaming a file), technically is a breaking change, even if this file only exists for internal stuff and doesn't affect the main function of the module. So the "exports" object tries to fix this issue by specifying the files that will be exposed. If any file is renamed, just change the exports entry, pointing to the new filename and that's all.
I'm a big fan of convention over configuration. So a solution for that could be to simply create a convention to make clear which files are "public" and which are internal.
One idea is to use underscores. The file _utils.ts
or the folder _core/
can be considered internal. This convention is more clear and transparent. Looking at the code, you can see what files you should import or not. It would also allow access to internal files if you need it under some circumstances, but it's clear in the module path that you're importing something that shouldn't, and if something fails after upgrading the module, it's your responsability.
In JSR is not allowed to import modules from URLs, only jsr:
and npm:
specifiers are allowed.
I do not agree with this decision, but anyway, I could bypass this limitation using dynamic imports: https://jsr.io/@vento/test/0.0.1/main.ts (sorry, I had to check. Feel free to remove the package).
So I'd remove this prohibition because, unless dynamic imports are also banned, it's impossible to prevent this.
JSR is not compatible with Git. This means that if you run deno publish
, all files in your current directory will be checked and uploaded to JSR, no matter what you have configured in .gitignore
or .gitattributes
.
This is especially dangerous in local contexts, where you can have sensitive files ignored by GIT but not by JSR, like .env
files, passwords, etc. After publishing vento
https://jsr.io/@vento/vento/0.10.2, I could see files like .DS_Store
or .gitignore
that shouldn't be there.
One solution could be to be able to preview the files before accepting the publication. But it's a manual step so can lead to human errors. The best solution to me is creating a publishing flow on top of git.