Skip to content

Commit

Permalink
refactor(regex): cleanup up index.ts and move matchStringStrategy to …
Browse files Browse the repository at this point in the history
…enum (#12525)
  • Loading branch information
secustor committed Dec 10, 2021
1 parent eeedade commit e8645bf
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 166 deletions.
168 changes: 2 additions & 166 deletions lib/manager/regex/index.ts
@@ -1,180 +1,16 @@
import { URL } from 'url';
import { logger } from '../../logger';
import { regEx } from '../../util/regex';
import * as template from '../../util/template';
import type {
CustomExtractConfig,
PackageDependency,
PackageFile,
Result,
} from '../types';
import type { ExtractionTemplate } from './types';
import { handleAny, handleCombination, handleRecursive } from './strategies';
import { validMatchFields } from './utils';

export const defaultConfig = {
pinDigests: false,
};

const validMatchFields = [
'depName',
'lookupName',
'currentValue',
'currentDigest',
'datasource',
'versioning',
'extractVersion',
'registryUrl',
'depType',
];

function regexMatchAll(regex: RegExp, content: string): RegExpMatchArray[] {
const matches: RegExpMatchArray[] = [];
let matchResult;
do {
matchResult = regex.exec(content);
if (matchResult) {
matches.push(matchResult);
}
} while (matchResult);
return matches;
}

function createDependency(
extractionTemplate: ExtractionTemplate,
config: CustomExtractConfig,
dep?: PackageDependency
): PackageDependency {
const dependency = dep || {};
const { groups, replaceString } = extractionTemplate;

function updateDependency(field: string, value: string): void {
switch (field) {
case 'registryUrl':
// check if URL is valid and pack inside an array
try {
const url = new URL(value).toString();
dependency.registryUrls = [url];
} catch (err) {
logger.warn({ value }, 'Invalid regex manager registryUrl');
}
break;
default:
dependency[field] = value;
break;
}
}

for (const field of validMatchFields) {
const fieldTemplate = `${field}Template`;
if (config[fieldTemplate]) {
try {
const compiled = template.compile(config[fieldTemplate], groups, false);
updateDependency(field, compiled);
} catch (err) {
logger.warn(
{ template: config[fieldTemplate] },
'Error compiling template for custom manager'
);
return null;
}
} else if (groups[field]) {
updateDependency(field, groups[field]);
}
}
dependency.replaceString = replaceString;
return dependency;
}

function handleAny(
content: string,
packageFile: string,
config: CustomExtractConfig
): PackageDependency[] {
return config.matchStrings
.map((matchString) => regEx(matchString, 'g'))
.flatMap((regex) => regexMatchAll(regex, content)) // match all regex to content, get all matches, reduce to single array
.map((matchResult) =>
createDependency(
{ groups: matchResult.groups, replaceString: matchResult[0] },
config
)
);
}

function mergeGroups(
mergedGroup: Record<string, string>,
secondGroup: Record<string, string>
): Record<string, string> {
return { ...mergedGroup, ...secondGroup };
}

export function mergeExtractionTemplate(
base: ExtractionTemplate,
addition: ExtractionTemplate
): ExtractionTemplate {
return {
groups: mergeGroups(base.groups, addition.groups),
replaceString: addition.replaceString ?? base.replaceString,
};
}

function handleCombination(
content: string,
packageFile: string,
config: CustomExtractConfig
): PackageDependency[] {
const matches = config.matchStrings
.map((matchString) => regEx(matchString, 'g'))
.flatMap((regex) => regexMatchAll(regex, content)); // match all regex to content, get all matches, reduce to single array

if (!matches.length) {
return [];
}

const extraction = matches
.map((match) => ({
groups: match.groups,
replaceString: match?.groups?.currentValue ? match[0] : undefined,
}))
.reduce((base, addition) => mergeExtractionTemplate(base, addition));
return [createDependency(extraction, config)];
}

function handleRecursive(
content: string,
packageFile: string,
config: CustomExtractConfig,
index = 0,
combinedGroups: Record<string, string> = {}
): PackageDependency[] {
const regexes = config.matchStrings.map((matchString) =>
regEx(matchString, 'g')
);
// abort if we have no matchString anymore
if (!regexes[index]) {
return [];
}
return regexMatchAll(regexes[index], content).flatMap((match) => {
// if we have a depName and a currentValue which have the minimal viable definition
if (match?.groups?.depName && match?.groups?.currentValue) {
return createDependency(
{
groups: mergeGroups(combinedGroups, match.groups),
replaceString: match[0],
},
config
);
}

return handleRecursive(
match[0],
packageFile,
config,
index + 1,
mergeGroups(combinedGroups, match.groups || {})
);
});
}

export function extractPackageFile(
content: string,
packageFile: string,
Expand Down
82 changes: 82 additions & 0 deletions lib/manager/regex/strategies.ts
@@ -0,0 +1,82 @@
import { regEx } from '../../util/regex';
import type { CustomExtractConfig, PackageDependency } from '../types';
import {
createDependency,
mergeExtractionTemplate,
mergeGroups,
regexMatchAll,
} from './utils';

export function handleAny(
content: string,
packageFile: string,
config: CustomExtractConfig
): PackageDependency[] {
return config.matchStrings
.map((matchString) => regEx(matchString, 'g'))
.flatMap((regex) => regexMatchAll(regex, content)) // match all regex to content, get all matches, reduce to single array
.map((matchResult) =>
createDependency(
{ groups: matchResult.groups, replaceString: matchResult[0] },
config
)
);
}

export function handleCombination(
content: string,
packageFile: string,
config: CustomExtractConfig
): PackageDependency[] {
const matches = config.matchStrings
.map((matchString) => regEx(matchString, 'g'))
.flatMap((regex) => regexMatchAll(regex, content)); // match all regex to content, get all matches, reduce to single array

if (!matches.length) {
return [];
}

const extraction = matches
.map((match) => ({
groups: match.groups,
replaceString: match?.groups?.currentValue ? match[0] : undefined,
}))
.reduce((base, addition) => mergeExtractionTemplate(base, addition));
return [createDependency(extraction, config)];
}

export function handleRecursive(
content: string,
packageFile: string,
config: CustomExtractConfig,
index = 0,
combinedGroups: Record<string, string> = {}
): PackageDependency[] {
const regexes = config.matchStrings.map((matchString) =>
regEx(matchString, 'g')
);
// abort if we have no matchString anymore
if (!regexes[index]) {
return [];
}
return regexMatchAll(regexes[index], content).flatMap((match) => {
// if we have a depName and a currentValue which have the minimal viable definition
if (match?.groups?.depName && match?.groups?.currentValue) {
return createDependency(
{
groups: mergeGroups(combinedGroups, match.groups),
replaceString: match[0],
},
config
);
}

return handleRecursive(
match[0],
packageFile,
config,
index + 1,
mergeGroups(combinedGroups, match.groups || {})
);
});
}
95 changes: 95 additions & 0 deletions lib/manager/regex/utils.ts
@@ -0,0 +1,95 @@
import { URL } from 'url';
import { logger } from '../../logger';
import * as template from '../../util/template';
import type { CustomExtractConfig, PackageDependency } from '../types';
import type { ExtractionTemplate } from './types';

export const validMatchFields = [
'depName',
'lookupName',
'currentValue',
'currentDigest',
'datasource',
'versioning',
'extractVersion',
'registryUrl',
'depType',
];

export function createDependency(
extractionTemplate: ExtractionTemplate,
config: CustomExtractConfig,
dep?: PackageDependency
): PackageDependency {
const dependency = dep || {};
const { groups, replaceString } = extractionTemplate;

function updateDependency(field: string, value: string): void {
switch (field) {
case 'registryUrl':
// check if URL is valid and pack inside an array
try {
const url = new URL(value).toString();
dependency.registryUrls = [url];
} catch (err) {
logger.warn({ value }, 'Invalid regex manager registryUrl');
}
break;
default:
dependency[field] = value;
break;
}
}

for (const field of validMatchFields) {
const fieldTemplate = `${field}Template`;
if (config[fieldTemplate]) {
try {
const compiled = template.compile(config[fieldTemplate], groups, false);
updateDependency(field, compiled);
} catch (err) {
logger.warn(
{ template: config[fieldTemplate] },
'Error compiling template for custom manager'
);
return null;
}
} else if (groups[field]) {
updateDependency(field, groups[field]);
}
}
dependency.replaceString = replaceString;
return dependency;
}

export function regexMatchAll(
regex: RegExp,
content: string
): RegExpMatchArray[] {
const matches: RegExpMatchArray[] = [];
let matchResult;
do {
matchResult = regex.exec(content);
if (matchResult) {
matches.push(matchResult);
}
} while (matchResult);
return matches;
}

export function mergeGroups(
mergedGroup: Record<string, string>,
secondGroup: Record<string, string>
): Record<string, string> {
return { ...mergedGroup, ...secondGroup };
}

export function mergeExtractionTemplate(
base: ExtractionTemplate,
addition: ExtractionTemplate
): ExtractionTemplate {
return {
groups: mergeGroups(base.groups, addition.groups),
replaceString: addition.replaceString ?? base.replaceString,
};
}

0 comments on commit e8645bf

Please sign in to comment.