Skip to content
This repository has been archived by the owner on May 15, 2024. It is now read-only.

Commit

Permalink
refactor(host-rules): Refactor matching logic (renovatebot#28482)
Browse files Browse the repository at this point in the history
  • Loading branch information
zharinov authored Apr 17, 2024
1 parent d9d744d commit e7d9c05
Showing 1 changed file with 53 additions and 30 deletions.
83 changes: 53 additions & 30 deletions lib/util/host-rules.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import is from '@sindresorhus/is';
import merge from 'deepmerge';
import type { SetRequired } from 'type-fest';
import { logger } from '../logger';
import type { CombinedHostRule, HostRule } from '../types';
import { clone } from './clone';
Expand Down Expand Up @@ -76,43 +77,59 @@ export interface HostRuleSearch {
url?: string;
}

function isEmptyRule(rule: HostRule): boolean {
function isEmpty(
rule: HostRule,
): rule is Omit<HostRule, 'hostType' | 'matchHost' | 'resolvedHost'> {
return !rule.hostType && !rule.resolvedHost;
}

function isHostTypeRule(rule: HostRule): boolean {
return !!rule.hostType && !rule.resolvedHost;
function isComplete(
rule: HostRule,
): rule is SetRequired<HostRule, 'hostType' | 'matchHost' | 'resolvedHost'> {
return !!rule.hostType && !!rule.resolvedHost;
}

function isHostOnlyRule(rule: HostRule): boolean {
return !rule.hostType && !!rule.matchHost;
function isOnlyHostType(
rule: HostRule,
): rule is Omit<
SetRequired<HostRule, 'hostType'>,
'matchHost' | 'resolvedHost'
> {
return !!rule.hostType && !rule.resolvedHost;
}

function isMultiRule(rule: HostRule): boolean {
return !!rule.hostType && !!rule.resolvedHost;
function isOnlyMatchHost(
rule: HostRule,
): rule is Omit<
SetRequired<HostRule, 'matchHost' | 'resolvedHost'>,
'hostType'
> {
return !rule.hostType && !!rule.matchHost;
}

function matchesHostType(rule: HostRule, search: HostRuleSearch): boolean {
return rule.hostType === search.hostType;
}
function matchesHost(url: string, matchHost: string): boolean {
if (validateUrl(url) && validateUrl(matchHost)) {
return url.startsWith(matchHost);
}

function matchesHost(rule: HostRule, search: HostRuleSearch): boolean {
// istanbul ignore if
if (!rule.matchHost) {
const parsedUrl = parseUrl(url);
if (!parsedUrl) {
return false;
}
if (search.url && validateUrl(rule.matchHost)) {
return search.url.startsWith(rule.matchHost);
}
const parsedUrl = search.url ? parseUrl(search.url) : null;
if (!parsedUrl?.hostname) {

const { hostname } = parsedUrl;
if (!hostname) {
return false;
}
const { hostname } = parsedUrl;
const dotPrefixedMatchHost = rule.matchHost.startsWith('.')
? rule.matchHost
: `.${rule.matchHost}`;
return hostname === rule.matchHost || hostname.endsWith(dotPrefixedMatchHost);

if (hostname === matchHost) {
return true;
}

const topLevelSuffix = matchHost.startsWith('.')
? matchHost
: `.${matchHost}`;
return hostname.endsWith(topLevelSuffix);
}

function prioritizeLongestMatchHost(rule1: HostRule, rule2: HostRule): number {
Expand All @@ -131,18 +148,23 @@ export function find(search: HostRuleSearch): CombinedHostRule {
let res: HostRule = {};
// First, apply empty rule matches
hostRules
.filter((rule) => isEmptyRule(rule))
.filter((rule) => isEmpty(rule))
.forEach((rule) => {
res = merge(res, rule);
});
// Next, find hostType-only matches
hostRules
.filter((rule) => isHostTypeRule(rule) && matchesHostType(rule, search))
.filter((rule) => isOnlyHostType(rule) && rule.hostType === search.hostType)
.forEach((rule) => {
res = merge(res, rule);
});
hostRules
.filter((rule) => isHostOnlyRule(rule) && matchesHost(rule, search))
.filter(
(rule) =>
isOnlyMatchHost(rule) &&
search.url &&
matchesHost(search.url, rule.matchHost),
)
.sort(prioritizeLongestMatchHost)
.forEach((rule) => {
res = merge(res, rule);
Expand All @@ -151,9 +173,10 @@ export function find(search: HostRuleSearch): CombinedHostRule {
hostRules
.filter(
(rule) =>
isMultiRule(rule) &&
matchesHostType(rule, search) &&
matchesHost(rule, search),
isComplete(rule) &&
rule.hostType === search.hostType &&
search.url &&
matchesHost(search.url, rule.matchHost),
)
.sort(prioritizeLongestMatchHost)
.forEach((rule) => {
Expand All @@ -175,7 +198,7 @@ export function hosts({ hostType }: { hostType: string }): string[] {
export function hostType({ url }: { url: string }): string | null {
return (
hostRules
.filter((rule) => matchesHost(rule, { url }))
.filter((rule) => rule.matchHost && matchesHost(url, rule.matchHost))
.sort(prioritizeLongestMatchHost)
.map((rule) => rule.hostType)
.filter(is.truthy)
Expand Down

0 comments on commit e7d9c05

Please sign in to comment.