import { PnpmError } from '@pnpm/error' export interface JsrSpec { jsrPkgName: string npmPkgName: string // A versionSelector may be a semver range (e.g. ^1.0.0), exact version (e.g. 2.3.4), or a dist-tag (e.g. "latest"). versionSelector?: string } export function parseJsrSpecifier (rawSpecifier: string, alias?: string): JsrSpec | null { if (!rawSpecifier.startsWith('jsr:')) return null rawSpecifier = rawSpecifier.substring('jsr:'.length) // syntax: jsr:@/[@] if (rawSpecifier[0] === '@') { const index = rawSpecifier.lastIndexOf('@') // syntax: jsr:@/ if (index === 0) { return { jsrPkgName: rawSpecifier, npmPkgName: jsrToNpmPackageName(rawSpecifier), } } // syntax: jsr:@/@ const jsrPkgName = rawSpecifier.substring(0, index) return { jsrPkgName, npmPkgName: jsrToNpmPackageName(jsrPkgName), versionSelector: rawSpecifier.substring(index + '@'.length), } } // syntax: jsr:@ (invalid) if (rawSpecifier.includes('@')) { throw new PnpmError('MISSING_JSR_PACKAGE_SCOPE', 'Package names from JSR must have a scope') } if (!alias) { throw new PnpmError('INVALID_JSR_SPECIFIER', `JSR specifier '${rawSpecifier}' is missing a package name`) } // syntax: jsr: return { versionSelector: rawSpecifier, jsrPkgName: alias, npmPkgName: jsrToNpmPackageName(alias), } } function jsrToNpmPackageName (jsrPkgName: string): string { if (jsrPkgName[0] !== '@') { throw new PnpmError('MISSING_JSR_PACKAGE_SCOPE', 'Package names from JSR must have a scope') } const sepIndex = jsrPkgName.indexOf('/') if (sepIndex === -1) { throw new PnpmError('INVALID_JSR_PACKAGE_NAME', `The package name '${jsrPkgName}' is invalid`) } const scope = jsrPkgName.substring(0, sepIndex) const name = jsrPkgName.substring(sepIndex + '/'.length) return `@jsr/${scope.substring(1)}__${name}` }