Publish pure ESM npm package written in TypeScript to JSR
fabon
Posted on May 12, 2024
Abstract
- You can publish a npm package to JSR
- JSR's API docs generation is delightful for a small OSS project, even if it doesn't use Deno
Introduction
JSR is a new package registry for JavaScript, which has compatibility to npm. JSR supports ES modules and TypeScript by default.
I'm developing vremel, an utility library for Temporal API (similar to date-fns for Date). It's a pure ESM package1 and written in TypeScript.
The package is formerly published only to official npm registry, but recently I started to publish it to JSR in addition. At first it was just for fun; I want to try a new thing. However, after I've published the package to JSR, I noticed that JSR saved me a headache: documentation.
API docs problem
JavaScript (TypeScript) ecosystem has various types of API docs generators. Maybe the most popular one is TypeDoc. While generating API docs itself is easy, hosting API docs is pretty hard. Publishing generated HTML to static hosting service like GitHub Pages is the method I adopted previously, but it's not an ideal solution because we can't view docs for older versions.
We can generate docs for all previous versions each time a new version is released, or save generated docs of older versions to external storage. Yes, it's technically possible, but complicated.
Now JSR have changed this situation. After publishing the package, we can view API docs of each version (similar to docs.rs in Rust or pkg.go.dev in Go). All we have to do is to write few lines of JSON. Optionally you can publish a package from GitHub Actions by adding only few lines to a workflow file. Any other setup (install packages, write config for document generator...) is not needed.
example: API docs for vremel v0.3.3
npm packages in JSR
JSR handles TypeScript source files very well, so publishing original TypeScript files is better than publishing generated JS and TypeScript definition files.
If the project has package.json
, then jsr publish
command can resolve builtin modules of Node, npm packages listed in the dependencies
field of package.json
. Also it can handle a .js
extension in relative imports from .ts
files (called 'sloppy imports' in Deno), which is the rule of pure ESM npm packages written in TypeScript2.
Actual steps
Create a package
Access https://jsr.io/new and create a package.
Write jsr.json
Write jsr.json
. See also JSR package config. For now you can specify subpath exports and files to be included or excluded.
In my case:
{
"name": "@fabon/vremel",
"version": "0.3.3",
"exports": {
".": "./src/index.ts",
"./duration": "./src/duration/index.ts"
},
"publish": {
"include": [
"CHANGELOG.md",
"README.md",
"LICENSE",
"src/**/*.ts",
"jsr.json"
],
"exclude": [
"**/*.test.ts"
]
}
}
Note that you have to include jsr.json
itself in publish.include
. Otherwise jsr publish
will fail (at least). In contrast, package.json
is not necessary.
Fix 'slow types'
You can run npx jsr publish --dry-run
to check whether the package is fine. You may encounter the error like missing explicit return type in the public API
due to JSR's restriction (See About "slow types"). In most cases all you have to do is just clarify return types.
// before
export function rand() {
return Math.random();
}
//after
export function rand(): number {
return Math.random();
}
You can pass --allow-slow-types
to jsr publish
if you have to depend on slow types, although it's not recommended by JSR.
In my case: Clarify return types explicitly · fabon-f/vremel@50d2963 · GitHub
Set up GitHub Actions
Of course you can publish to JSR from your PC, but it's better to publish from CI. Fortunately publishing to JSR from GitHub Actions is super easy, just follow official guides.
Note that if the package depends on external npm packages, you have to install dependencies to node_modules
before running jsr publish
. This is because jsr
command uses Node.js compatibility mode called BYONM when package.json
exist in the project.
Example of workflow file:
name: Publish the package
on:
push:
tags:
- "v*"
jobs:
jsr:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- name: Publish package
run: npx jsr publish
After pushing a release tag, the package will be published to JSR from GitHub Actions.
API docs generation in JSR
JSR uses Deno's document generator (deno_doc), which is under development and doesn't support all JSDoc tags yet3. So your JSDoc comment can be showed differently from your intention. You can run deno doc --unstable-sloppy-imports --html path/to/entrypoint.ts
to check how generate docs will look.
See also: How to document your JavaScript package from Deno team blog.
Conclusion
JSR is now under heavy development. I'm very looking forward to its rapid advance, especially in API docs generation.
-
Actually it's a dual package, but its structure and configurations are no different from a pure ESM package. ↩
-
These Node's style imports are rewritten to Deno's style internally by
jsr publish
command before publishing. ↩ -
For example
@description
and@summary
is not supported yet (in 2024-05-13); these tags will be simply ignored for now. ↩
Posted on May 12, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
February 22, 2023