| import { homedir } from 'os' |
| import { resolve } from 'path' |
| import { getErrnoCode } from '../errors.js' |
| import { getFsImplementation } from '../fsOperations.js' |
| import type { MarketplaceSource } from './schemas.js' |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export async function parseMarketplaceInput( |
| input: string, |
| ): Promise<MarketplaceSource | { error: string } | null> { |
| const trimmed = input.trim() |
| const fs = getFsImplementation() |
|
|
| |
| |
| |
| const sshMatch = trimmed.match( |
| /^([a-zA-Z0-9._-]+@[^:]+:.+?(?:\.git)?)(#(.+))?$/, |
| ) |
| if (sshMatch?.[1]) { |
| const url = sshMatch[1] |
| const ref = sshMatch[3] |
| return ref ? { source: 'git', url, ref } : { source: 'git', url } |
| } |
|
|
| |
| if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) { |
| |
| const fragmentMatch = trimmed.match(/^([^#]+)(#(.+))?$/) |
| const urlWithoutFragment = fragmentMatch?.[1] || trimmed |
| const ref = fragmentMatch?.[3] |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| if ( |
| urlWithoutFragment.endsWith('.git') || |
| urlWithoutFragment.includes('/_git/') |
| ) { |
| return ref |
| ? { source: 'git', url: urlWithoutFragment, ref } |
| : { source: 'git', url: urlWithoutFragment } |
| } |
| |
| let url: URL |
| try { |
| url = new URL(urlWithoutFragment) |
| } catch (_err) { |
| |
| |
| return { source: 'url', url: urlWithoutFragment } |
| } |
|
|
| if (url.hostname === 'github.com' || url.hostname === 'www.github.com') { |
| const match = url.pathname.match(/^\/([^/]+\/[^/]+?)(\/|\.git|$)/) |
| if (match?.[1]) { |
| |
| |
| const gitUrl = urlWithoutFragment.endsWith('.git') |
| ? urlWithoutFragment |
| : `${urlWithoutFragment}.git` |
| return ref |
| ? { source: 'git', url: gitUrl, ref } |
| : { source: 'git', url: gitUrl } |
| } |
| } |
| return { source: 'url', url: urlWithoutFragment } |
| } |
|
|
| |
| |
| |
| const isWindows = process.platform === 'win32' |
| const isWindowsPath = |
| isWindows && |
| (trimmed.startsWith('.\\') || |
| trimmed.startsWith('..\\') || |
| /^[a-zA-Z]:[/\\]/.test(trimmed)) |
| if ( |
| trimmed.startsWith('./') || |
| trimmed.startsWith('../') || |
| trimmed.startsWith('/') || |
| trimmed.startsWith('~') || |
| isWindowsPath |
| ) { |
| const resolvedPath = resolve( |
| trimmed.startsWith('~') ? trimmed.replace(/^~/, homedir()) : trimmed, |
| ) |
|
|
| |
| |
| |
| let stats |
| try { |
| stats = await fs.stat(resolvedPath) |
| } catch (e: unknown) { |
| const code = getErrnoCode(e) |
| return { |
| error: |
| code === 'ENOENT' |
| ? `Path does not exist: ${resolvedPath}` |
| : `Cannot access path: ${resolvedPath} (${code ?? e})`, |
| } |
| } |
|
|
| if (stats.isFile()) { |
| if (resolvedPath.endsWith('.json')) { |
| return { source: 'file', path: resolvedPath } |
| } else { |
| return { |
| error: `File path must point to a .json file (marketplace.json), but got: ${resolvedPath}`, |
| } |
| } |
| } else if (stats.isDirectory()) { |
| return { source: 'directory', path: resolvedPath } |
| } else { |
| return { |
| error: `Path is neither a file nor a directory: ${resolvedPath}`, |
| } |
| } |
| } |
|
|
| |
| |
| |
| if (trimmed.includes('/') && !trimmed.startsWith('@')) { |
| if (trimmed.includes(':')) { |
| return null |
| } |
| |
| const fragmentMatch = trimmed.match(/^([^#@]+)(?:[#@](.+))?$/) |
| const repo = fragmentMatch?.[1] || trimmed |
| const ref = fragmentMatch?.[2] |
| |
| return ref ? { source: 'github', repo, ref } : { source: 'github', repo } |
| } |
|
|
| |
| |
|
|
| return null |
| } |
|
|