diff --git a/lib/core/fluent-url.ts b/lib/core/fluent-url.ts index 3529074..f0fff34 100644 --- a/lib/core/fluent-url.ts +++ b/lib/core/fluent-url.ts @@ -13,7 +13,7 @@ import type { } from '@/shared/types' export class FluentUrl { - private config: FluentUrlConfig + private readonly config: FluentUrlConfig private readonly protocol?: string private readonly hostname?: string private readonly paths: string[] @@ -76,7 +76,7 @@ export class FluentUrl { public clone( options?: Partial, - config?: FluentUrlConfig, + config?: Partial, ): FluentUrl { return new FluentUrl( mergeOptions( diff --git a/lib/core/serializer/url-serializer.ts b/lib/core/serializer/url-serializer.ts index 1b93273..cab9bdc 100644 --- a/lib/core/serializer/url-serializer.ts +++ b/lib/core/serializer/url-serializer.ts @@ -1,9 +1,74 @@ -import type { UrlSerializer } from '@/shared/types' +import type { FLuentUrlPlainObject, UrlSerializer } from '@/shared/types' -export const parseUrl: UrlSerializer['parseUrl'] = () => { - throw new Error('Not implemented') +const URLTypesObj = { + absolute: 'absolute', + relative: 'relative', + protocolRelative: 'protocol-relative', +} as const + +type URLTypes = (typeof URLTypesObj)[keyof typeof URLTypesObj] + +const defaultProtocol = 'https' +const defaultHost = 'example.com' + +export const parseUrl: UrlSerializer['parseUrl'] = ({ str, parseQuery }) => { + const urlString = str.trim() + + const urlType: URLTypes = urlString.startsWith('//') + ? URLTypesObj.protocolRelative + : urlString.startsWith('/') + ? URLTypesObj.relative + : URLTypesObj.absolute + + let newUrlString = urlString + + if (urlType === URLTypesObj.protocolRelative) { + newUrlString = `${defaultProtocol}:${newUrlString}` + } + + if (urlType === URLTypesObj.relative) { + newUrlString = `${defaultProtocol}://${defaultHost}${newUrlString}` + } + + let url: URL + + try { + url = new URL(newUrlString) + } catch { + throw new Error('Invalid URL') + } + + const urlPlainObject: FLuentUrlPlainObject = { + paths: [], + queries: {}, + } + + if (urlType === URLTypesObj.absolute) { + urlPlainObject.protocol = + url.protocol !== '' ? url.protocol.replace(':', '') : undefined + } + + if ( + urlType === URLTypesObj.absolute || + urlType === URLTypesObj.protocolRelative + ) { + urlPlainObject.hostname = url.hostname || undefined + } + + urlPlainObject.paths = + url.pathname === '/' ? [] : url.pathname.split('/').filter((p) => p !== '') + + urlPlainObject.port = url.port !== '' ? Number(url.port) : undefined + + urlPlainObject.fragment = + url.hash !== '' ? url.hash.replace('#', '') : undefined + + urlPlainObject.queries = + url.search !== '' ? parseQuery(url.search) : Object.create(null) + + return urlPlainObject } -export const stringifyUrl: UrlSerializer['stringifyUrl'] = () => { +export const stringifyUrl: UrlSerializer['stringifyUrl'] = (_args) => { throw new Error('Not implemented') } diff --git a/lib/shared/types/index.ts b/lib/shared/types/index.ts index 667d612..fe9488a 100644 --- a/lib/shared/types/index.ts +++ b/lib/shared/types/index.ts @@ -17,11 +17,11 @@ export interface QuerySerializer { export interface UrlSerializer { parseUrl: (args: { str: string - parseQuery?: QuerySerializer['parseQuery'] + parseQuery: QuerySerializer['parseQuery'] }) => FLuentUrlPlainObject stringifyUrl: (args: { obj: FLuentUrlPlainObject - stringifyQuery?: QuerySerializer['stringifyQuery'] + stringifyQuery: QuerySerializer['stringifyQuery'] }) => string }