JSR Publishing Audit
Comprehensive audit guide for JSR (JavaScript Registry) compliance and scoring optimization.
Understanding the JSR Score
JSR assigns packages a quality score from 0-100%, displayed with color coding:
- Red: Below 60%
- Orange: 60-90%
- Green: 90%+ (target this)
The score directly influences search ranking. View any package's breakdown at jsr.io/@scope/package/score.
The 9 Scoring Factors
| Category | Factor | Impact |
|---|---|---|
| Documentation | Has README or module doc | High |
| Has examples in README/module doc | High | |
| Has module docs in all entrypoints | High | |
| Has docs for most symbols | High | |
| Best Practices | No slow types | Medium |
| Has provenance (SLSA attestation) | Medium | |
| Discoverability | Has description (≤250 chars) | Low |
| Compatibility | At least one runtime marked compatible | Low |
| At least two runtimes compatible | Low |
Key insight: Documentation carries the heaviest weight. Comprehensive JSDoc + README + @module tags will score higher than perfect types with minimal docs.
Audit Checklist
| Check | Command/Location | What to Look For |
|---|---|---|
| Required metadata | deno.json | name, version, exports fields |
| Scoped package name | name field | Format: @scope/package-name |
| Package description | description field | ≤250 characters for discoverability |
| Valid exports | exports field | Entry points exist and are correct |
| No slow types | deno publish --dry-run | No slow type warnings |
| Clean file list | deno publish --dry-run | Only intended files included |
| ESM only | Source files | No CommonJS (module.exports, require()) |
| Module documentation | Entry point files | @module JSDoc tag present |
| Symbol documentation | Exported symbols | JSDoc with @param, @returns, @example |
Audit Process
Step 1: Check Required Metadata
Read deno.json (or jsr.json) and verify:
{
"name": "@scope/package-name",
"version": "1.0.0",
"description": "Concise package description under 250 characters",
"exports": "./mod.ts"
}
Package name rules:
- Must start with
@scope/ - Lowercase letters, numbers, hyphens only
- 2-40 characters (excluding scope)
Config file choice:
- Deno projects: Use
deno.json - Node/Bun/other: Use
jsr.json - Never split config between both files
Step 2: Verify Exports
Check all entry points exist:
{
"exports": {
".": "./mod.ts",
"./utils": "./src/utils/mod.ts"
}
}
For each entry point, confirm:
- File exists at specified path
- Exports are properly defined
- Type annotations are explicit (no slow types)
- Has
@moduleJSDoc tag
Step 3: Audit Documentation Quality
Module-level documentation (top of entry points):
/**
* A module providing string utilities for common transformations.
*
* @example
* ```ts
* import { camelCase } from "@scope/cases";
* camelCase("hello world"); // "helloWorld"
* ```
*
* @module
*/
Symbol documentation:
/**
* Converts a string to camelCase format.
*
* @param input - The string to convert
* @returns The camelCase formatted string
*
* @example
* ```ts
* camelCase("hello world"); // "helloWorld"
* camelCase("foo-bar-baz"); // "fooBarBaz"
* ```
*/
export function camelCase(input: string): string {
// ...
}
JSDoc tags reference:
@param name - description- Document parameters@returns description- Document return value@example- Runnable code examples (use triple backticks)@deprecated message- Mark deprecated APIs@see SymbolName- Cross-reference related symbols@throws ErrorType- Document thrown errors{@link SymbolName}- Inline links{@linkcode SymbolName}- Inline links with monospace
Step 4: Run Dry-Run Verification
deno publish --dry-run --allow-dirty
This reveals:
- Slow types: Exports without explicit type annotations
- File list: Everything that would be published
- Metadata errors: Invalid name, version, etc.
Step 5: Audit the File List
Review dry-run output for files that should NOT be published:
Common offenders:
.claude/,.zed/,.vscode/- IDE settings.github/,.gitlab/- CI/CD configs.mise.toml,.tool-versions- Local toolingcoverage/- Test coverage datadocs/,*.md(except LICENSE/README) - Documentationdeno.lock- Lock files*.test.ts,**/test_utils/**- Test filessonar-project.properties,*.config.js- Build configs.env,*.local.*- Local configuration
Step 6: Configure Publish Filtering
Use include (whitelist) + exclude (filter):
{
"publish": {
"include": [
"LICENSE",
"README.md",
"deno.json",
"mod.ts",
"src/**/*.ts"
],
"exclude": [
"**/*.test.ts",
"**/test_utils/**"
]
}
}
Why both?
includewhitelists only intended filesexcludefilters test files fromsrc/**/*.tsglobexcludetakes precedence when both match
Step 7: Verify Clean Output
Run dry-run again. Only these should appear:
LICENSEREADME.mddeno.json- Entry point files (
mod.ts, etc.) - Source code (
src/**/*.tsminus tests)
Fixing Slow Types
Slow types are exports without explicit type annotations. They prevent JSR from generating .d.ts files efficiently and degrade npm compatibility by 1.5-2x slower type checking.
Function Return Types
// SLOW - inferred return type
export function greet(name: string) {
return "Hello, " + name + "!";
}
// FIXED - explicit return type
export function greet(name: string): string {
return "Hello, " + name + "!";
}
Constants
// SLOW - inferred type
export const GLOBAL_ID = crypto.randomUUID();
// FIXED - explicit annotation
export const GLOBAL_ID: string = crypto.randomUUID();
Class Properties
// SLOW - inferred property type
export class Config {
timeout = 5000;
}
// FIXED - explicit property type
export class Config {
timeout: number = 5000;
}
Detection Commands
# Catch slow types before publishing
deno publish --dry-run
# Validate documentation generation
deno doc --lint
Note: TypeScript 5.5's isolatedDeclarations mode produces code that automatically satisfies JSR's no-slow-types requirement.
Prohibited Patterns
JSR enforces ESM-only architecture:
| Prohibited | Alternative |
|---|---|
require() | import |
module.exports | export |
declare global | Module-scoped types |
declare module | Module-scoped types |
| HTTP imports | jsr:, npm:, node: specifiers only |
Multiple Entry Points
For packages with multiple exports:
{
"exports": {
".": "./mod.ts",
"./providers": "./providers.ts",
"./utils": "./src/utils/mod.ts"
}
}
Users import as:
import { main } from "@scope/pkg";
import { OpenAI } from "@scope/pkg/providers";
import { helper } from "@scope/pkg/utils";
Each entry point should have its own @module JSDoc tag.
Dependencies
Declare in imports:
{
"imports": {
"@std/assert": "jsr:@std/assert@^1.0.0",
"zod": "npm:zod@^3.22.0"
}
}
Supported specifiers:
jsr:@scope/pkg@version- JSR packagesnpm:package@version- npm packagesnode:module- Node.js built-ins
CI Publishing with Provenance
GitHub Actions OIDC enables tokenless publishing with automatic SLSA provenance:
name: Publish to JSR
on:
push:
tags: ['v*']
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # Required for OIDC and provenance
steps:
- uses: actions/checkout@v5
- uses: denoland/setup-deno@v2
with:
deno-version: v2.x
- run: deno publish
Requirements for provenance:
- Link package to GitHub repo in JSR settings
- Include
id-token: writepermission - Provenance generated automatically
No API tokens needed - JSR uses GitHub OIDC authentication.
Audit Report Template
After completing the audit:
## JSR Publishing Audit Results
| Check | Status |
|-------|--------|
| Required metadata | ✓ / ✗ |
| Scoped package name | ✓ / ✗ |
| Package description | ✓ / ✗ |
| Valid exports | ✓ / ✗ |
| No slow types | ✓ / ✗ |
| Clean file list | ✓ / ✗ |
| ESM only | ✓ / ✗ |
| Module documentation | ✓ / ✗ |
| Symbol documentation | ✓ / ✗ |
### Estimated Score Impact
- Documentation: X/4 factors
- Best Practices: X/2 factors
- Discoverability: X/1 factors
- Compatibility: X/2 factors
### Files Published
[List from dry-run]
### Issues Found
[List any problems]
### Recommendations
[Suggested fixes prioritized by score impact]
Quick Commands
# Full audit dry-run
deno publish --dry-run --allow-dirty
# Validate documentation
deno doc --lint
# Type check
deno check **/*.ts
# Lint
deno lint
# Format
deno fmt
# Publish (when ready)
deno publish
Known Limitations (2024-2025)
- Private packages: Not yet available (most requested feature)
- JSX publishing: Not supported
- HTTP imports: Prohibited
- Complex inference: Libraries like Zod/ArkType may require
--allow-slow-types
