vite-plugin-mkcert
Version:
Provide certificates for vite's https dev service
169 lines (133 loc) • 3.69 kB
text/typescript
import process from 'node:process'
import { fetch, ProxyAgent, type RequestInit } from 'undici'
import { error_log } from './logger'
type ResponseType = 'json' | 'arraybuffer' | 'text'
type RequestConfig = {
method?: string
url: string
data?: unknown
headers?: Record<string, string>
responseType?: ResponseType
proxy?: string
onDownloadProgress?: (progress: {
loaded: number
total?: number
percent?: number
}) => void
}
type RequestResponse<T = unknown> = {
data: T
}
const getProxyFromEnv = () => {
const value =
process.env.HTTPS_PROXY ||
process.env.https_proxy ||
process.env.HTTP_PROXY ||
process.env.http_proxy
return value?.trim() || undefined
}
const proxyAgentMap = new Map<string, ProxyAgent>()
const getDispatcher = (proxy?: string) => {
const proxyUrl = proxy?.trim() || getProxyFromEnv()
if (!proxyUrl) {
return undefined
}
let dispatcher = proxyAgentMap.get(proxyUrl)
if (!dispatcher) {
dispatcher = new ProxyAgent(proxyUrl)
proxyAgentMap.set(proxyUrl, dispatcher)
}
return dispatcher
}
const readArrayBufferWithProgress = async (
response: Response,
onDownloadProgress?: RequestConfig['onDownloadProgress']
) => {
if (!onDownloadProgress || !response.body) {
return Buffer.from(await response.arrayBuffer())
}
const totalHeader = response.headers.get('content-length')
const total = totalHeader ? Number(totalHeader) : undefined
const reader = response.body.getReader()
const chunks: Uint8Array[] = []
let loaded = 0
while (true) {
const { done, value } = await reader.read()
if (done) {
break
}
if (value) {
chunks.push(value)
loaded += value.byteLength
onDownloadProgress({
loaded,
total,
percent: total ? (loaded / total) * 100 : undefined
})
}
}
return Buffer.concat(chunks.map(chunk => Buffer.from(chunk)), loaded)
}
const parseResponse = async (
response: Response,
responseType?: ResponseType,
onDownloadProgress?: RequestConfig['onDownloadProgress']
) => {
if (responseType === 'arraybuffer') {
return readArrayBufferWithProgress(response, onDownloadProgress)
}
if (responseType === 'text') {
return await response.text()
}
const contentType = response.headers.get('content-type')
if (responseType === 'json' || contentType?.includes('application/json')) {
return await response.json()
}
return await response.text()
}
async function doRequest<T = any>(
config: RequestConfig
): Promise<RequestResponse<T>> {
const {
method = 'GET',
url,
data,
headers,
responseType,
proxy,
onDownloadProgress
} = config
const init: RequestInit = { method }
if (data) {
init.headers = { 'Content-Type': 'application/json', ...headers }
init.body = JSON.stringify(data)
} else if (headers) {
init.headers = headers
}
const dispatcher = getDispatcher(proxy)
if (dispatcher) {
; (init as any).dispatcher = dispatcher
}
try {
const response = await fetch(url, init as RequestInit)
if (!response.ok) {
throw new Error(
`Request failed [${method}] ${url} (${response.status} ${response.statusText})`
)
}
const responseData = await parseResponse(
response,
responseType,
onDownloadProgress
)
return { data: responseData as T }
} catch (error) {
error_log('Request error: %o', error)
throw error
}
}
const request = Object.assign(doRequest, {
get: <T = any>(url: string, config?: Omit<RequestConfig, 'url' | 'method'>) =>
doRequest<T>({ ...config, method: 'GET', url })
})
export default request