feat: enhance configuration handling and add type definitions for query and URL serializers
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -142,3 +142,4 @@ dist
|
|||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
vite.config.ts.timestamp-*
|
vite.config.ts.timestamp-*
|
||||||
.vite/
|
.vite/
|
||||||
|
*.local
|
||||||
|
|||||||
@@ -4,28 +4,32 @@ import { deepClone } from '@/shared/helpers/deep-clone'
|
|||||||
import type {
|
import type {
|
||||||
FLuentUrlPlainObject,
|
FLuentUrlPlainObject,
|
||||||
FluentUrlConfig,
|
FluentUrlConfig,
|
||||||
QuerySerializer,
|
NormalizeOptionsArgs,
|
||||||
|
PlainObjectConstraint,
|
||||||
} from '@/shared/types'
|
} from '@/shared/types'
|
||||||
|
|
||||||
export function normalizeConfig(
|
export function normalizeConfig<
|
||||||
config?: Partial<FluentUrlConfig>,
|
PQO extends PlainObjectConstraint,
|
||||||
): FluentUrlConfig {
|
SQO extends PlainObjectConstraint,
|
||||||
|
>(config?: Partial<FluentUrlConfig<PQO, SQO>>): FluentUrlConfig<PQO, SQO> {
|
||||||
return {
|
return {
|
||||||
parseQuery: config?.parseQuery ?? parseQuery,
|
parseQuery: config?.parseQuery ?? parseQuery,
|
||||||
|
parseQueryOptions: config?.parseQueryOptions,
|
||||||
stringifyQuery: config?.stringifyQuery ?? stringifyQuery,
|
stringifyQuery: config?.stringifyQuery ?? stringifyQuery,
|
||||||
|
stringifyQueryOptions: config?.stringifyQueryOptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeOptions(args: {
|
export function normalizeOptions<T extends PlainObjectConstraint>({
|
||||||
urlOptions?: Partial<FLuentUrlPlainObject> | string
|
urlOrOptions,
|
||||||
parseQuery: QuerySerializer['parseQuery']
|
parseQuery,
|
||||||
}): FLuentUrlPlainObject {
|
parseQueryOptions,
|
||||||
const { urlOptions: urlOrOptions, parseQuery } = args
|
}: NormalizeOptionsArgs<T>): FLuentUrlPlainObject {
|
||||||
|
|
||||||
if (typeof urlOrOptions === 'string') {
|
if (typeof urlOrOptions === 'string') {
|
||||||
return parseUrl({
|
return parseUrl({
|
||||||
str: urlOrOptions,
|
str: urlOrOptions,
|
||||||
parseQuery: parseQuery,
|
parseQuery,
|
||||||
|
parseQueryOptions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,13 +43,18 @@ export function normalizeOptions(args: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mergeConfig(
|
export function mergeConfig<
|
||||||
current: FluentUrlConfig,
|
PQO extends PlainObjectConstraint,
|
||||||
merge?: Partial<FluentUrlConfig>,
|
SQO extends PlainObjectConstraint,
|
||||||
): FluentUrlConfig {
|
>(
|
||||||
|
current: FluentUrlConfig<PQO, SQO>,
|
||||||
|
merge?: Partial<FluentUrlConfig<PQO, SQO>>,
|
||||||
|
): FluentUrlConfig<PQO, SQO> {
|
||||||
return {
|
return {
|
||||||
parseQuery: merge?.parseQuery ?? current.parseQuery,
|
parseQuery: merge?.parseQuery ?? current.parseQuery,
|
||||||
|
parseQueryOptions: merge?.parseQueryOptions,
|
||||||
stringifyQuery: merge?.stringifyQuery ?? current.stringifyQuery,
|
stringifyQuery: merge?.stringifyQuery ?? current.stringifyQuery,
|
||||||
|
stringifyQueryOptions: merge?.stringifyQueryOptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,17 @@ import { deepClone } from '@/shared/helpers/deep-clone'
|
|||||||
import type {
|
import type {
|
||||||
FLuentUrlPlainObject,
|
FLuentUrlPlainObject,
|
||||||
FluentUrlConfig,
|
FluentUrlConfig,
|
||||||
|
ParseQueryOptions,
|
||||||
|
PlainObjectConstraint,
|
||||||
QueriesPlainObject,
|
QueriesPlainObject,
|
||||||
|
StringifyQueryOptions,
|
||||||
} from '@/shared/types'
|
} from '@/shared/types'
|
||||||
|
|
||||||
export class FluentUrl {
|
export class FluentUrl<
|
||||||
private readonly config: FluentUrlConfig
|
PQO extends PlainObjectConstraint = ParseQueryOptions,
|
||||||
|
SQO extends PlainObjectConstraint = StringifyQueryOptions,
|
||||||
|
> {
|
||||||
|
private readonly config: FluentUrlConfig<PQO, SQO>
|
||||||
private readonly protocol?: string
|
private readonly protocol?: string
|
||||||
private readonly hostname?: string
|
private readonly hostname?: string
|
||||||
private readonly paths: string[]
|
private readonly paths: string[]
|
||||||
@@ -23,14 +29,15 @@ export class FluentUrl {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
urlOrOptions?: Partial<FLuentUrlPlainObject> | string,
|
urlOrOptions?: Partial<FLuentUrlPlainObject> | string,
|
||||||
config?: Partial<FluentUrlConfig>,
|
config?: Partial<FluentUrlConfig<PQO, SQO>>,
|
||||||
) {
|
) {
|
||||||
this.config = normalizeConfig(config)
|
this.config = normalizeConfig(config)
|
||||||
|
|
||||||
const { protocol, hostname, paths, port, fragment, queries } =
|
const { protocol, hostname, paths, port, fragment, queries } =
|
||||||
normalizeOptions({
|
normalizeOptions({
|
||||||
urlOptions: urlOrOptions,
|
urlOrOptions,
|
||||||
parseQuery: this.config.parseQuery,
|
parseQuery: this.config.parseQuery,
|
||||||
|
parseQueryOptions: this.config.parseQueryOptions,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.protocol = protocol
|
this.protocol = protocol
|
||||||
@@ -76,8 +83,8 @@ export class FluentUrl {
|
|||||||
|
|
||||||
public clone(
|
public clone(
|
||||||
options?: Partial<FLuentUrlPlainObject>,
|
options?: Partial<FLuentUrlPlainObject>,
|
||||||
config?: Partial<FluentUrlConfig>,
|
config?: Partial<FluentUrlConfig<PQO, SQO>>,
|
||||||
): FluentUrl {
|
): FluentUrl<PQO, SQO> {
|
||||||
return new FluentUrl(
|
return new FluentUrl(
|
||||||
mergeOptions(
|
mergeOptions(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
import type { QuerySerializer } from '@/shared/types'
|
import type {
|
||||||
|
ParseQuery,
|
||||||
|
PlainObjectConstraint,
|
||||||
|
StringifyQuery,
|
||||||
|
} from '@/shared/types'
|
||||||
|
|
||||||
export const parseQuery: QuerySerializer['parseQuery'] = () => {
|
export function parseQuery<T extends PlainObjectConstraint>(): ReturnType<
|
||||||
|
ParseQuery<T>
|
||||||
|
> {
|
||||||
throw new Error('Not implemented')
|
throw new Error('Not implemented')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stringifyQuery: QuerySerializer['stringifyQuery'] = () => {
|
export function stringifyQuery<T extends PlainObjectConstraint>(): ReturnType<
|
||||||
|
StringifyQuery<T>
|
||||||
|
> {
|
||||||
throw new Error('Not implemented')
|
throw new Error('Not implemented')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import type { FLuentUrlPlainObject, UrlSerializer } from '@/shared/types'
|
import type {
|
||||||
|
FLuentUrlPlainObject,
|
||||||
|
ParseUrlArgs,
|
||||||
|
PlainObjectConstraint,
|
||||||
|
StringifyUrlArgs,
|
||||||
|
} from '@/shared/types'
|
||||||
|
|
||||||
const URLTypesObj = {
|
const URLTypesObj = {
|
||||||
absolute: 'absolute',
|
absolute: 'absolute',
|
||||||
@@ -11,7 +16,11 @@ type URLTypes = (typeof URLTypesObj)[keyof typeof URLTypesObj]
|
|||||||
const defaultProtocol = 'https'
|
const defaultProtocol = 'https'
|
||||||
const defaultHost = 'example.com'
|
const defaultHost = 'example.com'
|
||||||
|
|
||||||
export const parseUrl: UrlSerializer['parseUrl'] = ({ str, parseQuery }) => {
|
export function parseUrl<T extends PlainObjectConstraint>({
|
||||||
|
str,
|
||||||
|
parseQuery,
|
||||||
|
parseQueryOptions,
|
||||||
|
}: ParseUrlArgs<T>): FLuentUrlPlainObject {
|
||||||
const urlString = str.trim()
|
const urlString = str.trim()
|
||||||
|
|
||||||
const urlType: URLTypes = urlString.startsWith('//')
|
const urlType: URLTypes = urlString.startsWith('//')
|
||||||
@@ -64,11 +73,15 @@ export const parseUrl: UrlSerializer['parseUrl'] = ({ str, parseQuery }) => {
|
|||||||
url.hash !== '' ? url.hash.replace('#', '') : undefined
|
url.hash !== '' ? url.hash.replace('#', '') : undefined
|
||||||
|
|
||||||
urlPlainObject.queries =
|
urlPlainObject.queries =
|
||||||
url.search !== '' ? parseQuery(url.search) : Object.create(null)
|
url.search !== ''
|
||||||
|
? parseQuery(url.search, parseQueryOptions)
|
||||||
|
: Object.create(null)
|
||||||
|
|
||||||
return urlPlainObject
|
return urlPlainObject
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stringifyUrl: UrlSerializer['stringifyUrl'] = (_args) => {
|
export function stringifyUrl<T extends PlainObjectConstraint>(
|
||||||
|
_args: StringifyUrlArgs<T>,
|
||||||
|
): string {
|
||||||
throw new Error('Not implemented')
|
throw new Error('Not implemented')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,23 +9,50 @@ export interface FLuentUrlPlainObject {
|
|||||||
queries: QueriesPlainObject
|
queries: QueriesPlainObject
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QuerySerializer {
|
export type PlainObjectConstraint = Record<string, any>
|
||||||
parseQuery: (str: string) => QueriesPlainObject
|
|
||||||
stringifyQuery: (obj: QueriesPlainObject) => string
|
export interface ParseQueryOptions extends PlainObjectConstraint {
|
||||||
|
[k: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UrlSerializer {
|
export type ParseQuery<T extends PlainObjectConstraint> = (
|
||||||
parseUrl: (args: {
|
str: string,
|
||||||
str: string
|
options?: T,
|
||||||
parseQuery: QuerySerializer['parseQuery']
|
) => QueriesPlainObject
|
||||||
}) => FLuentUrlPlainObject
|
|
||||||
stringifyUrl: (args: {
|
export interface StringifyQueryOptions extends PlainObjectConstraint {
|
||||||
obj: FLuentUrlPlainObject
|
[k: string]: any
|
||||||
stringifyQuery: QuerySerializer['stringifyQuery']
|
|
||||||
}) => string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FluentUrlConfig {
|
export type StringifyQuery<T extends PlainObjectConstraint> = (
|
||||||
parseQuery: QuerySerializer['parseQuery']
|
obj: QueriesPlainObject,
|
||||||
stringifyQuery: QuerySerializer['stringifyQuery']
|
options?: T,
|
||||||
|
) => string
|
||||||
|
|
||||||
|
export interface ParseUrlArgs<T extends PlainObjectConstraint> {
|
||||||
|
str: string
|
||||||
|
parseQuery: ParseQuery<T>
|
||||||
|
parseQueryOptions?: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StringifyUrlArgs<T extends PlainObjectConstraint> {
|
||||||
|
obj: FLuentUrlPlainObject
|
||||||
|
stringifyQuery: StringifyQuery<T>
|
||||||
|
stringifyQueryOptions?: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FluentUrlConfig<
|
||||||
|
PQO extends PlainObjectConstraint,
|
||||||
|
SQO extends PlainObjectConstraint,
|
||||||
|
> {
|
||||||
|
parseQuery: ParseQuery<PQO>
|
||||||
|
parseQueryOptions?: PQO
|
||||||
|
stringifyQuery: StringifyQuery<SQO>
|
||||||
|
stringifyQueryOptions?: SQO
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NormalizeOptionsArgs<T extends PlainObjectConstraint> {
|
||||||
|
urlOrOptions?: Partial<FLuentUrlPlainObject> | string
|
||||||
|
parseQuery: ParseQuery<T>
|
||||||
|
parseQueryOptions?: T
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rimraf ./dist && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
"build": "rimraf ./dist && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
||||||
|
"type-check": "tsc --noEmit --skipLibCheck",
|
||||||
"prepare": "pnpm build",
|
"prepare": "pnpm build",
|
||||||
"test": "vitest"
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user