-
-
Notifications
You must be signed in to change notification settings - Fork 432
Expand file tree
/
Copy pathparse-package-param.ts
More file actions
85 lines (80 loc) · 2.8 KB
/
parse-package-param.ts
File metadata and controls
85 lines (80 loc) · 2.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* Parsed package parameters from URL path segments.
*/
export interface ParsedPackageParams {
/** The npm package name (e.g., "vue", "@nuxt/kit") */
packageName: string
/** The version if specified (e.g., "3.4.0"), undefined otherwise */
version: string | undefined
/** Remaining path segments after the version (e.g., for file paths) */
rest: string[]
}
/**
* Parse package name, optional version, and remaining path from URL segments.
*
* Supports these URL patterns:
* - `/pkg` → { packageName: "pkg", version: undefined, rest: [] }
* - `/pkg/v/1.2.3` → { packageName: "pkg", version: "1.2.3", rest: [] }
* - `/pkg/v/1.2.3/src/index.ts` → { packageName: "pkg", version: "1.2.3", rest: ["src", "index.ts"] }
* - `/@scope/pkg` → { packageName: "@scope/pkg", version: undefined, rest: [] }
* - `/@scope/pkg/v/1.2.3` → { packageName: "@scope/pkg", version: "1.2.3", rest: [] }
*
* @param pkgParam - The raw package parameter from the URL (e.g., "vue/v/3.4.0/src/index.ts")
* @returns Parsed package name, optional version, and remaining path segments
*
* @example
* ```ts
* parsePackageParam('vue')
* // { packageName: 'vue', version: undefined, rest: [] }
*
* parsePackageParam('vue/v/3.4.0')
* // { packageName: 'vue', version: '3.4.0', rest: [] }
*
* parsePackageParam('@nuxt/kit/v/1.0.0/src/index.ts')
* // { packageName: '@nuxt/kit', version: '1.0.0', rest: ['src', 'index.ts'] }
* ```
*/
/**
* Parse a "pkg@version" specifier string into name and optional version.
* Handles scoped packages correctly (the scope `@` is not treated as a version separator).
*
* @example
* ```ts
* parsePackageSpecifier('esbuild@0.25.12')
* // { name: 'esbuild', version: '0.25.12' }
*
* parsePackageSpecifier('@angular/core@^18')
* // { name: '@angular/core', version: '^18' }
*
* parsePackageSpecifier('react')
* // { name: 'react' }
* ```
*/
export function parsePackageSpecifier(input: string): { name: string; version?: string } {
const atIndex = input.startsWith('@') ? input.indexOf('@', 1) : input.indexOf('@')
if (atIndex > 0) {
const version = input.slice(atIndex + 1)
if (version) return { name: input.slice(0, atIndex), version }
}
return { name: input }
}
export function parsePackageParam(pkgParam: string): ParsedPackageParams {
const segments = pkgParam.split('/')
let vIndex = segments.indexOf('v')
// If we encounter ".../v/v/...", treat the second "v" as the version delimiter.
if (segments[vIndex] === 'v' && segments[vIndex + 1] === 'v') {
vIndex++
}
if (vIndex !== -1 && vIndex < segments.length - 1) {
return {
packageName: segments.slice(0, vIndex).join('/'),
version: segments[vIndex + 1],
rest: segments.slice(vIndex + 2),
}
}
return {
packageName: segments.join('/'),
version: undefined,
rest: [],
}
}