Last active
February 2, 2025 09:44
-
-
Save fsubal/b484a6ab3e254ae15aad4474dd4b87d7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { compile } from 'path-to-regexp' | |
type UrlParams = Record<string, string | number> | |
function normarizeUrlParams(input: UrlParams): Record<string, string> { | |
return Object.fromEntries( | |
Object.entries(input).map(([key, value]) => [key, value.toString()]) | |
) | |
} | |
export abstract class AppUrl<P extends UrlParams, Q extends {}> { | |
static get baseUrl() { | |
if (typeof location !== 'undefined') { | |
return location.protocol + '://' + location.host | |
} else if (typeof process !== 'undefined' && process.env.APP_HOST) { | |
return process.env.APP_HOST | |
} else { | |
throw new Error( | |
'[AppUrl] Could not resolve baseUrl. ' + | |
'Neither window.location nor process.env.APP_HOST is available.' | |
) | |
} | |
} | |
static dynamic<P extends UrlParams, Q extends UrlParams = {}>( | |
pathTemplate: `${string}:${keyof P & string}${string}` | |
): AppDynamicUrl<P, Q> { | |
return new AppDynamicUrl<P, Q>((params?: P) => compile(pathTemplate)(params ? normarizeUrlParams(params) : {}), this.baseUrl) | |
} | |
static static<Q extends UrlParams = {}>(pathTemplate: string): AppStaticUrl<Q> { | |
return new AppStaticUrl<Q>(() => pathTemplate, this.baseUrl) | |
} | |
protected constructor(protected pathTemplate: (params?: P) => string, protected baseUrl: string) {} | |
protected addQueryParams(url: URL, queryParams: Q) { | |
const searchParams = new URLSearchParams(Object.entries(queryParams)) | |
url.search = searchParams.toString() | |
} | |
abstract url(...args: unknown[]): URL | |
abstract path(...args: unknown[]): string | |
} | |
class AppDynamicUrl<P extends UrlParams, Q extends {}> extends AppUrl<P, Q> { | |
url(pathParams: P, queryParams?: Q): URL { | |
const url = new URL(this.pathTemplate(pathParams), this.baseUrl) | |
if (queryParams) { | |
this.addQueryParams(url, queryParams) | |
} | |
return url | |
} | |
path(pathParams: P, queryParams?: Q): string { | |
const { pathname, search } = this.url(pathParams, queryParams) | |
return pathname + '?' + search | |
} | |
} | |
class AppStaticUrl<Q extends {}> extends AppUrl<never, Q> { | |
url(queryParams?: Q): URL { | |
const url = new URL(this.pathTemplate(), this.baseUrl) | |
if (queryParams) { | |
this.addQueryParams(url, queryParams) | |
} | |
return url | |
} | |
path(queryParams?: Q): string { | |
const { pathname, search } = this.url(queryParams) | |
return pathname + '?' + search | |
} | |
} | |
const templateShow = AppUrl.dynamic<{ id: 'a', name: string }>('/templates/:name/') | |
templateShow.path({ id: 'a' }, { utm_source: 'app' }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment