[READ-ONLY] a fast, modern browser for the npm registry
at main 75 lines 2.4 kB view raw
1import spdxLicenseIds from 'spdx-license-list/spdx-simple.json' 2 3/** 4 * Set of all valid SPDX license identifiers. 5 * Sourced from spdx-license-list package which stays up-to-date with 6 * the official SPDX license list. 7 * 8 * @see https://spdx.org/licenses/ 9 */ 10export const SPDX_LICENSE_IDS: Set<string> = new Set(spdxLicenseIds) 11 12/** 13 * Check if a license identifier is a valid SPDX license. 14 * @see https://spdx.org/licenses/ 15 */ 16export function isValidSpdxLicense(license: string): boolean { 17 return SPDX_LICENSE_IDS.has(license) 18} 19 20/** 21 * Generate an SPDX license URL for the given license identifier. 22 * Returns null if the license is not a valid SPDX identifier. 23 * @see https://spdx.org/licenses/ 24 */ 25export function getSpdxLicenseUrl(license: string | undefined): string | null { 26 if (!license) return null 27 const trimmed = license.trim() 28 if (!SPDX_LICENSE_IDS.has(trimmed)) return null 29 return `https://spdx.org/licenses/${trimmed}.html` 30} 31 32/** 33 * Token types for parsed license expressions 34 */ 35export interface LicenseToken { 36 type: 'license' | 'operator' 37 value: string 38 url?: string 39} 40 41/** 42 * Parse an SPDX license expression into tokens. 43 * Handles compound expressions like "MIT OR Apache-2.0", "(MIT AND Zlib)". 44 * Strips parentheses for cleaner display. 45 * 46 * @example 47 * parseLicenseExpression('MIT') // [{ type: 'license', value: 'MIT', url: '...' }] 48 * parseLicenseExpression('MIT OR Apache-2.0') 49 * // [{ type: 'license', value: 'MIT', url: '...' }, { type: 'operator', value: 'OR' }, { type: 'license', value: 'Apache-2.0', url: '...' }] 50 */ 51export function parseLicenseExpression(expression: string): LicenseToken[] { 52 const result: LicenseToken[] = [] 53 // Match operators first (OR, AND, WITH), then license IDs - ignore parentheses 54 // Operators must be checked first to avoid being captured as license identifiers 55 const pattern = /\b(OR|AND|WITH)\b|([A-Za-z0-9.\-+]+)/g 56 let match 57 58 while ((match = pattern.exec(expression)) !== null) { 59 if (match[1]) { 60 // Operator (OR, AND, WITH) 61 result.push({ type: 'operator', value: match[1] }) 62 } else if (match[2]) { 63 // License identifier 64 const id = match[2] 65 const isValid = isValidSpdxLicense(id) 66 result.push({ 67 type: 'license', 68 value: id, 69 url: isValid ? `https://spdx.org/licenses/${id}.html` : undefined, 70 }) 71 } 72 } 73 74 return result 75}