Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion packages/cli-kit/src/public/common/url.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {isValidURL, safeParseURL} from './url.js'
import {extractHost, extractMyshopifyHandle, isValidURL, safeParseURL} from './url.js'
import {describe, expect, test} from 'vitest'

describe('isValidURL', () => {
Expand Down Expand Up @@ -57,3 +57,34 @@ describe('safeParseURL', () => {
expect(result).toBeUndefined()
})
})

describe('extractHost', () => {
test('returns the hostname for a full URL', () => {
expect(extractHost('https://Shop.MyShopify.com/admin')).toBe('shop.myshopify.com')
})

test('strips the scheme and path for a bare host string', () => {
expect(extractHost('shop.myshopify.com/admin')).toBe('shop.myshopify.com')
})

test('returns undefined for null/undefined/empty input', () => {
expect(extractHost(null)).toBeUndefined()
expect(extractHost(undefined)).toBeUndefined()
expect(extractHost('')).toBeUndefined()
})
})

describe('extractMyshopifyHandle', () => {
test('extracts the subdomain from a myshopify.com URL', () => {
expect(extractMyshopifyHandle('https://my-shop.myshopify.com')).toBe('my-shop')
})

test('returns undefined when the host is not a myshopify.com domain', () => {
expect(extractMyshopifyHandle('https://example.com')).toBeUndefined()
})

test('returns undefined for null/undefined input', () => {
expect(extractMyshopifyHandle(null)).toBeUndefined()
expect(extractMyshopifyHandle(undefined)).toBeUndefined()
})
})
29 changes: 29 additions & 0 deletions packages/cli-kit/src/public/common/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,32 @@ export function safeParseURL(url: string): URL | undefined {
return undefined
}
}

/**
* Extracts the lowercased hostname from a URL-shaped string. Tolerates
* bare hosts (without a scheme) and inputs that come back from APIs as
* either `https://shop.myshopify.com` or `shop.myshopify.com`.
*
* @param value - A URL or bare host string, possibly null/undefined.
* @returns The lowercased hostname, or undefined when the input is empty.
*/
export function extractHost(value: string | null | undefined): string | undefined {
if (!value) return undefined
const lowered = value.toLowerCase()
const parsed = safeParseURL(lowered)
if (parsed) return parsed.hostname
return lowered.replace(/^https?:\/\//, '').split('/')[0]
}

/**
* Extracts the subdomain handle from a `*.myshopify.com` URL or host.
*
* @param value - A URL or host string, possibly null/undefined.
* @returns The myshopify subdomain handle, or undefined when the input isn't a `*.myshopify.com` URL.
*/
export function extractMyshopifyHandle(value: string | null | undefined): string | undefined {
const host = extractHost(value)
if (!host) return undefined
const match = host.match(/^([^.]+)\.myshopify\.com$/)
return match ? match[1] : undefined
}
Loading