Files
mocoapp-browser-extension/src/js/utils/urlMatcher.js
Manuel Bouza 061a3d9a89 feature/host-overrides (#161)
* configurable host overrides

* base host overrides on name of service instead of key and hide the options by default

* added unit tests

* review changes

* Refactor options

* Refactor

* Update Readme

* Pump version and update Changelog

Co-authored-by: Tobias Jacksteit <me@xtj7.de>
2020-06-15 17:14:31 +02:00

137 lines
3.5 KiB
JavaScript

import UrlPattern from "url-pattern"
import { isFunction, isUndefined, compose, toPairs, map, pipe, isNil, reduce } from "lodash/fp"
import { asArray } from "./index"
import queryString from "query-string"
function parseUrl(url) {
const urlObject = new URL(url)
const { origin, pathname, search } = urlObject
let { hash } = urlObject
const query = {
...queryString.parse(search),
...queryString.parse(hash),
}
if (hash) {
hash = hash.match(/#[^&]+/)[0]
}
return { origin, pathname, hash, query }
}
function extractQueryParams(queryParams, query) {
return toPairs(queryParams).reduce((acc, [key, params]) => {
const param = asArray(params).find((param) => !isNil(query[param]))
if (param) {
acc[key] = query[param]
}
return acc
}, {})
}
const createEvaluator = (args) => (fnOrValue) => {
if (isUndefined(fnOrValue)) {
return
}
if (isFunction(fnOrValue)) {
return fnOrValue(...args)
}
return fnOrValue
}
const prepareHostForRegExp = (host) => {
return host.replace(":", "\\:")
}
const replaceHostInPattern = (host, pattern) => {
if (typeof pattern === "string") {
return pattern.replace(":host:", prepareHostForRegExp(host))
} else if (pattern instanceof RegExp) {
return new RegExp(pattern.source.replace(":host:", prepareHostForRegExp(host)))
} else {
return pattern
}
}
const parseServices = compose(
map(([key, config]) => ({
...config,
key,
patterns: config.urlPatterns.map((pattern) => {
if (Array.isArray(pattern)) {
return new UrlPattern(...pattern.map((p) => replaceHostInPattern(config.host, p)))
}
return new UrlPattern(replaceHostInPattern(config.host, pattern))
}),
})),
toPairs,
)
export const createEnhancer = (document) => (service) => {
if (!service) {
return
}
const match = service.match
const args = [document, service, match]
const evaluate = createEvaluator(args)
return {
...service,
id: evaluate(service.id),
description: evaluate(service.description),
projectId: evaluate(service.projectId),
taskId: evaluate(service.taskId),
position: service.position || { right: "calc(2rem + 5px)" },
}
}
const applyHostOverrides = (remoteServices, hostOverrides) =>
pipe(
toPairs,
reduce((acc, [key, remoteService]) => {
acc[key] = {
...remoteService,
key,
host: hostOverrides[remoteService.name] || remoteService.host,
}
return acc
}, {}),
)(remoteServices)
export const createMatcher = (remoteServices, hostOverrides) => {
const services = parseServices(applyHostOverrides(remoteServices, hostOverrides))
return (tabUrl) => {
const { origin, pathname, hash, query } = parseUrl(tabUrl)
const url = `${origin}${pathname}${hash}`
const service = services.find((service) => {
return service.patterns.some((pattern) => pattern.match(url))
})
if (!service) {
return
}
const pattern = service.patterns.find((pattern) => pattern.match(url))
let match = pattern.match(url)
if (service.queryParams) {
const extractedQueryParams = extractQueryParams(service.queryParams, query)
match = { ...extractedQueryParams, ...match }
}
return {
...match,
...service,
url: tabUrl,
match,
}
}
}
export const createServiceFinder = (remoteServices, hostOverrides) => (document) => {
const matcher = createMatcher(remoteServices, hostOverrides)
const enhancer = createEnhancer(document)
return pipe(matcher, enhancer)
}